slugforge 4.0.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 +7 -0
- data/README.md +316 -0
- data/bin/slugforge +9 -0
- data/lib/slugforge.rb +19 -0
- data/lib/slugforge/build.rb +4 -0
- data/lib/slugforge/build/build_project.rb +31 -0
- data/lib/slugforge/build/export_upstart.rb +85 -0
- data/lib/slugforge/build/package.rb +63 -0
- data/lib/slugforge/cli.rb +125 -0
- data/lib/slugforge/commands.rb +130 -0
- data/lib/slugforge/commands/build.rb +20 -0
- data/lib/slugforge/commands/config.rb +24 -0
- data/lib/slugforge/commands/deploy.rb +383 -0
- data/lib/slugforge/commands/project.rb +21 -0
- data/lib/slugforge/commands/tag.rb +148 -0
- data/lib/slugforge/commands/wrangler.rb +142 -0
- data/lib/slugforge/configuration.rb +125 -0
- data/lib/slugforge/helper.rb +186 -0
- data/lib/slugforge/helper/build.rb +46 -0
- data/lib/slugforge/helper/config.rb +37 -0
- data/lib/slugforge/helper/enumerable.rb +46 -0
- data/lib/slugforge/helper/fog.rb +90 -0
- data/lib/slugforge/helper/git.rb +89 -0
- data/lib/slugforge/helper/path.rb +76 -0
- data/lib/slugforge/helper/project.rb +86 -0
- data/lib/slugforge/models/host.rb +233 -0
- data/lib/slugforge/models/host/fog_host.rb +33 -0
- data/lib/slugforge/models/host/hostname_host.rb +9 -0
- data/lib/slugforge/models/host/ip_address_host.rb +9 -0
- data/lib/slugforge/models/host_group.rb +65 -0
- data/lib/slugforge/models/host_group/aws_tag_group.rb +22 -0
- data/lib/slugforge/models/host_group/ec2_instance_group.rb +21 -0
- data/lib/slugforge/models/host_group/hostname_group.rb +16 -0
- data/lib/slugforge/models/host_group/ip_address_group.rb +16 -0
- data/lib/slugforge/models/host_group/security_group_group.rb +20 -0
- data/lib/slugforge/models/logger.rb +36 -0
- data/lib/slugforge/models/tag_manager.rb +125 -0
- data/lib/slugforge/slugins.rb +125 -0
- data/lib/slugforge/version.rb +9 -0
- data/scripts/post-install.sh +143 -0
- data/scripts/unicorn-shepherd.sh +305 -0
- data/spec/fixtures/array.yaml +3 -0
- data/spec/fixtures/fog_credentials.yaml +4 -0
- data/spec/fixtures/invalid_syntax.yaml +1 -0
- data/spec/fixtures/one.yaml +3 -0
- data/spec/fixtures/two.yaml +3 -0
- data/spec/fixtures/valid.yaml +4 -0
- data/spec/slugforge/commands/deploy_spec.rb +72 -0
- data/spec/slugforge/commands_spec.rb +33 -0
- data/spec/slugforge/configuration_spec.rb +200 -0
- data/spec/slugforge/helper/fog_spec.rb +81 -0
- data/spec/slugforge/helper/git_spec.rb +152 -0
- data/spec/slugforge/models/host_group/aws_tag_group_spec.rb +54 -0
- data/spec/slugforge/models/host_group/ec2_instance_group_spec.rb +51 -0
- data/spec/slugforge/models/host_group/hostname_group_spec.rb +20 -0
- data/spec/slugforge/models/host_group/ip_address_group_spec.rb +54 -0
- data/spec/slugforge/models/host_group/security_group_group_spec.rb +52 -0
- data/spec/slugforge/models/tag_manager_spec.rb +75 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/env.rb +3 -0
- data/spec/support/example_groups/configuration_writer.rb +24 -0
- data/spec/support/example_groups/helper_provider.rb +10 -0
- data/spec/support/factories.rb +18 -0
- data/spec/support/fog.rb +15 -0
- data/spec/support/helpers.rb +18 -0
- data/spec/support/mock_logger.rb +6 -0
- data/spec/support/ssh.rb +8 -0
- data/spec/support/streams.rb +13 -0
- data/templates/foreman/master.conf.erb +21 -0
- data/templates/foreman/process-master.conf.erb +2 -0
- data/templates/foreman/process.conf.erb +19 -0
- metadata +344 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
module Slugforge
|
2
|
+
module Helper
|
3
|
+
module Build
|
4
|
+
def verify_procfile_exists!
|
5
|
+
unless File.exist?(project_path('Procfile'))
|
6
|
+
logger.say_status :warning, "Slugforge should normally be run in a project with a Procfile (#{project_path('Procfile')})", :yellow
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def ruby_version_specified?
|
11
|
+
options[:ruby] and !options[:ruby].empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_ruby_version_file?
|
15
|
+
File.exist?(project_path('.ruby-version'))
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_ruby_version_from_file
|
19
|
+
ruby_version = read_from_file
|
20
|
+
if ruby_version.nil? or ruby_version.empty?
|
21
|
+
raise error_class, "You don't have a ruby version specified in your .ruby-version file!!! Why you no set ruby version."
|
22
|
+
else
|
23
|
+
return ruby_version
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def read_from_file
|
28
|
+
begin
|
29
|
+
File.read(project_path('.ruby-version')).delete("\n")
|
30
|
+
rescue Exception => e
|
31
|
+
raise error_class, "There were issues reading the .ruby-version file. Make sure it exists in the project path and it has valid content, #{e}."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def package_file_name
|
36
|
+
"#{project_name}-#{date_stamp}-#{git_sha}.slug"
|
37
|
+
end
|
38
|
+
|
39
|
+
def date_stamp
|
40
|
+
# Keep this as a class variable so the date stamp remains the same throughought the lifecycle of the app.
|
41
|
+
@@date_stamp ||= Time.now.strftime('%Y%m%d%H%M%S')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Slugforge
|
2
|
+
module Helper
|
3
|
+
module Config
|
4
|
+
def self.included(base)
|
5
|
+
base.class_option :'aws-access-key-id', :type => :string, :aliases => '-I', :group => :config,
|
6
|
+
:desc => 'The AWS Access ID to use for hosts and buckets, unless overridden'
|
7
|
+
base.class_option :'aws-secret-key', :type => :string, :aliases => '-S', :group => :config,
|
8
|
+
:desc => 'The AWS Secret Key to use for hosts and buckets, unless overridden'
|
9
|
+
base.class_option :'aws-region', :type => :string, :group => :config,
|
10
|
+
:desc => 'The AWS region to use for EC2 instances and buckets'
|
11
|
+
base.class_option :'slug-bucket', :type => :string, :group => :config,
|
12
|
+
:desc => 'The S3 bucket to store the slugs and tags in'
|
13
|
+
base.class_option :'aws-session-token', :type => :string, :group => :config,
|
14
|
+
:desc => 'The AWS Session Token to use for hosts and buckets'
|
15
|
+
|
16
|
+
base.class_option :project, :type => :string, :aliases => '-P', :group => :config,
|
17
|
+
:desc => 'The name of the project as it exists in Slugforge. See the Project Naming section in the main help.'
|
18
|
+
|
19
|
+
base.class_option :'ssh-username', :type => :string, :aliases => '-u', :group => :config,
|
20
|
+
:desc => 'The account used to log in to the host (requires sudo privileges)'
|
21
|
+
|
22
|
+
base.class_option :'disable-slugins', :type => :boolean, :group => :config,
|
23
|
+
:desc => 'Disable slugin loading'
|
24
|
+
|
25
|
+
base.class_option :verbose, :type => :boolean, :aliases => '-V', :group => :runtime,
|
26
|
+
:desc => 'Display verbose output'
|
27
|
+
base.class_option :json, :type => :boolean, :aliases => '-j', :group => :runtime,
|
28
|
+
:desc => 'Display JSON output'
|
29
|
+
|
30
|
+
# Options intended for slugforge developers
|
31
|
+
base.class_option :test, :type => :boolean, :group => :runtime, :hide => true,
|
32
|
+
:desc => 'Test mode. Behaves like --pretend but triggers notifications and side effects as if a real action was taken.'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Enumerable
|
2
|
+
def parallel_map
|
3
|
+
queue = Queue.new
|
4
|
+
|
5
|
+
self.map do |item|
|
6
|
+
Thread.new do
|
7
|
+
# NOTE: You can not do anything that is not thread safe in this block...
|
8
|
+
queue << yield(item)
|
9
|
+
end
|
10
|
+
end.each(&:join)
|
11
|
+
|
12
|
+
[].tap do |results|
|
13
|
+
results << queue.pop until queue.empty?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def parallel_map_with_index
|
18
|
+
queue = Queue.new
|
19
|
+
|
20
|
+
self.map.with_index do |item, index|
|
21
|
+
Thread.new do
|
22
|
+
# NOTE: You can not do anything that is not thread safe in this block...
|
23
|
+
queue << yield(item, index)
|
24
|
+
end
|
25
|
+
end.each(&:join)
|
26
|
+
|
27
|
+
[].tap do |results|
|
28
|
+
results << queue.pop until queue.empty?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def ordered_parallel_map
|
33
|
+
queue = Queue.new
|
34
|
+
|
35
|
+
self.map.with_index do |item, index|
|
36
|
+
Thread.new do
|
37
|
+
# NOTE: You can not do anything that is not thread safe in this block...
|
38
|
+
queue << [index, yield(item)]
|
39
|
+
end
|
40
|
+
end.each(&:join)
|
41
|
+
|
42
|
+
[].tap do |results|
|
43
|
+
results << queue.pop until queue.empty?
|
44
|
+
end.sort.map {|index, item| item }
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'fog'
|
2
|
+
|
3
|
+
module Slugforge
|
4
|
+
module Helper
|
5
|
+
module Fog
|
6
|
+
def compute
|
7
|
+
@compute ||= ::Fog::Compute.new(aws_credentials.merge({
|
8
|
+
:region => config.aws_region,
|
9
|
+
:provider => 'AWS'
|
10
|
+
}))
|
11
|
+
end
|
12
|
+
|
13
|
+
def autoscaling
|
14
|
+
@autoscaling ||= ::Fog::AWS::AutoScaling.new(aws_credentials)
|
15
|
+
end
|
16
|
+
|
17
|
+
def s3
|
18
|
+
@s3 ||= ::Fog::Storage.new(aws_credentials.merge({
|
19
|
+
:provider => 'AWS'
|
20
|
+
}))
|
21
|
+
end
|
22
|
+
|
23
|
+
def aws_credentials
|
24
|
+
{
|
25
|
+
:aws_access_key_id => verify_aws_config(config.aws_access_key, 'access key'),
|
26
|
+
:aws_secret_access_key => verify_aws_config(config.aws_secret_key, 'secret key'),
|
27
|
+
:aws_session_token => config.aws_session_token
|
28
|
+
}.reject{ |_,v| v.nil? }
|
29
|
+
end
|
30
|
+
|
31
|
+
def aws_bucket
|
32
|
+
config.slug_bucket || raise(error_class, "You must specify a slug bucket in your configuration")
|
33
|
+
end
|
34
|
+
|
35
|
+
def expiring_url(file, expiration=nil)
|
36
|
+
expiration ||= Time.now + 60*60
|
37
|
+
file.url(expiration)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create a temporary AWS session
|
41
|
+
# @return [Hash] hash containing :access_key_id, :secret_access_key, :session_token
|
42
|
+
def aws_session(duration = 30)
|
43
|
+
@aws_session ||= begin
|
44
|
+
sts = ::Fog::AWS::STS.new(aws_credentials)
|
45
|
+
|
46
|
+
# Request a token for the user that has permissions masked to a single S3 bucket and only lasts a short time
|
47
|
+
token = sts.get_federation_token( username, bucket_policy, duration * 60 ) # session duration in minutes
|
48
|
+
|
49
|
+
{
|
50
|
+
aws_access_key_id: token.body['AccessKeyId'],
|
51
|
+
aws_secret_access_key: token.body['SecretAccessKey'],
|
52
|
+
aws_session_token: token.body['SessionToken'],
|
53
|
+
aws_region: config.aws_region
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def username
|
60
|
+
`whoami`.chomp
|
61
|
+
end
|
62
|
+
|
63
|
+
def verify_aws_config(variable, message)
|
64
|
+
raise error_class, "AWS #{message} is required to access AWS" unless variable
|
65
|
+
variable
|
66
|
+
end
|
67
|
+
|
68
|
+
def bucket_policy(bucket = aws_bucket)
|
69
|
+
{
|
70
|
+
"Version" => "2012-10-17",
|
71
|
+
"Statement" => [
|
72
|
+
{
|
73
|
+
"Action" => ["s3:*"],
|
74
|
+
"Effect" => "Allow",
|
75
|
+
"Resource" => ["arn:aws:s3:::#{bucket}/*"]
|
76
|
+
},
|
77
|
+
{
|
78
|
+
"Action" => [
|
79
|
+
"s3:ListBucket"
|
80
|
+
],
|
81
|
+
"Effect" => "Allow",
|
82
|
+
"Resource" => [ "arn:aws:s3:::#{bucket}" ]
|
83
|
+
}
|
84
|
+
]
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Slugforge
|
2
|
+
module Helper
|
3
|
+
module Git
|
4
|
+
|
5
|
+
SHA_MAX_LENGTH = 10
|
6
|
+
|
7
|
+
def git_inside_work_tree?
|
8
|
+
return @git_inside_work_tree unless @git_inside_work_tree.nil?
|
9
|
+
@git_inside_work_tree = git_command('rev-parse --is-inside-work-tree') == 'true'
|
10
|
+
end
|
11
|
+
|
12
|
+
def git_user
|
13
|
+
@git_user ||= git_command('config github.user')
|
14
|
+
end
|
15
|
+
|
16
|
+
def git_account
|
17
|
+
return nil unless git_inside_work_tree? && !git_url.empty?
|
18
|
+
@git_account ||= git_url.match(%r|[:/]([^/]+)/[^/]+(\.git)?$|)[1]
|
19
|
+
end
|
20
|
+
|
21
|
+
def git_repository
|
22
|
+
return nil unless git_inside_work_tree? && !git_url.empty?
|
23
|
+
@git_repository ||= git_url.match(%r|/([^/]+?)(\.git)?$|)[1]
|
24
|
+
end
|
25
|
+
|
26
|
+
def git_branch
|
27
|
+
return nil unless git_inside_work_tree?
|
28
|
+
@git_branch ||= begin
|
29
|
+
symbolic_ref = git_command('symbolic-ref HEAD')
|
30
|
+
symbolic_ref.sub(%r|^refs/heads/|, '')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def git_remote
|
35
|
+
return nil unless git_inside_work_tree?
|
36
|
+
@git_remote ||= git_command("config branch.#{git_branch}.remote")
|
37
|
+
# If we are headless just assume origin so that we can still detect other values
|
38
|
+
@git_remote.empty? ? 'origin' : @git_remote
|
39
|
+
end
|
40
|
+
|
41
|
+
def git_remote_sha(opts = {})
|
42
|
+
return nil unless git_inside_work_tree?
|
43
|
+
sha_length = opts[:sha_length] || SHA_MAX_LENGTH
|
44
|
+
url = opts[:url] || git_url
|
45
|
+
branch = opts[:branch] || git_branch
|
46
|
+
|
47
|
+
@git_remote_sha = begin
|
48
|
+
if @git_remote_sha.nil? || opts[:memoize] == false
|
49
|
+
output = git_command("ls-remote #{url} #{branch}").split(" ").first
|
50
|
+
output =~ /^[0-9a-f]{40}$/i ? output : nil
|
51
|
+
else
|
52
|
+
@git_remote_sha
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
return @git_remote_sha.slice(0...sha_length) unless @git_remote_sha.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
def git_sha(opts = {})
|
60
|
+
raise error_class, "SHA can't be detected as this is not a git repository" unless git_inside_work_tree?
|
61
|
+
sha_length = opts[:sha_length] || SHA_MAX_LENGTH
|
62
|
+
@git_sha ||= git_command('rev-parse HEAD').chomp
|
63
|
+
@git_sha.slice(0...sha_length)
|
64
|
+
end
|
65
|
+
|
66
|
+
def git_url
|
67
|
+
return '' unless git_inside_work_tree?
|
68
|
+
@git_url ||= git_command("config remote.#{git_remote}.url")
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_git_url(account, repository)
|
72
|
+
account ||= git_account
|
73
|
+
repository ||= git_repository
|
74
|
+
"git@github.com:#{account}/#{repository}.git"
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def git_command(cmd)
|
79
|
+
path = options[:path] ? "cd '#{options[:path]}' &&" : ""
|
80
|
+
`#{path} git #{cmd} 2> /dev/null`.chomp
|
81
|
+
end
|
82
|
+
|
83
|
+
def git_info
|
84
|
+
Hash[methods.select { |m| m.to_s =~/^git_/ }.map { |m| [ m.to_s, send(m) ] }]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
module Slugforge
|
4
|
+
module Helper
|
5
|
+
module Path
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
base.source_root base.templates_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def project_path(*paths)
|
12
|
+
File.join(project_root, *paths)
|
13
|
+
end
|
14
|
+
|
15
|
+
def project_root
|
16
|
+
return @locate_project unless @locate_project.nil?
|
17
|
+
if options[:path] && Dir.exist?(File.expand_path(options[:path]))
|
18
|
+
return File.expand_path(options[:path])
|
19
|
+
end
|
20
|
+
|
21
|
+
path = File.expand_path(Dir.pwd)
|
22
|
+
paths = path.split('/')
|
23
|
+
until paths.empty?
|
24
|
+
if Dir.exist?(File.join(*paths, '.git'))
|
25
|
+
@locate_project = File.join(*paths)
|
26
|
+
return @locate_project
|
27
|
+
end
|
28
|
+
|
29
|
+
paths.pop
|
30
|
+
end
|
31
|
+
raise error_class, "Invalid path. Unable to find a .git project anywhere in path #{path}. Specify a path with --path."
|
32
|
+
end
|
33
|
+
|
34
|
+
def upstart_dir
|
35
|
+
@upstart_conf_dir ||= project_path('deploy', 'upstart').tap do |dir|
|
36
|
+
FileUtils.mkdir_p(dir)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def scripts_dir(*paths)
|
42
|
+
File.join(self.class.scripts_dir, *paths)
|
43
|
+
end
|
44
|
+
|
45
|
+
def templates_dir(*paths)
|
46
|
+
File.join(self.class.templates_dir, *paths)
|
47
|
+
end
|
48
|
+
|
49
|
+
def deploy_dir(*paths)
|
50
|
+
@deploy_dir ||= File.join('/opt', 'apps', project_name)
|
51
|
+
File.join(@deploy_dir, *paths)
|
52
|
+
end
|
53
|
+
|
54
|
+
def release_dir(*paths)
|
55
|
+
deploy_dir('releases', sha)
|
56
|
+
end
|
57
|
+
|
58
|
+
def system_with_path(cmd, path=nil)
|
59
|
+
path ||= options[:path]
|
60
|
+
cwd_command = path ? "cd #{path} && " : ""
|
61
|
+
::Bundler.with_clean_env { system("#{cwd_command}#{cmd}") }
|
62
|
+
end
|
63
|
+
|
64
|
+
module ClassMethods
|
65
|
+
def scripts_dir
|
66
|
+
@scripts_dir ||= File.expand_path('../../../scripts', File.dirname(__FILE__))
|
67
|
+
end
|
68
|
+
|
69
|
+
def templates_dir
|
70
|
+
@templates_dir ||= File.expand_path('../../../templates', File.dirname(__FILE__))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Slugforge
|
2
|
+
module Helper
|
3
|
+
module Project
|
4
|
+
def initialize(args=[], options={}, config={})
|
5
|
+
super
|
6
|
+
# Tracking the current command so we can be safe when creating projects
|
7
|
+
@current_command = config[:current_command]
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
def tag_manager
|
12
|
+
@tag_manager ||= TagManager.new(:s3 => s3, :bucket => aws_bucket)
|
13
|
+
end
|
14
|
+
|
15
|
+
def bucket
|
16
|
+
@bucket ||= s3.directories.get(aws_bucket)
|
17
|
+
end
|
18
|
+
|
19
|
+
def project_name
|
20
|
+
# First one to return a value wins!
|
21
|
+
@project_name ||= config.project || git_repository
|
22
|
+
raise error_class, "Could not determine project name. This repository probably doesn't have an upstream branch yet. Please push your code, or specify `--project` when running slugforge." if @project_name.nil?
|
23
|
+
@project_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def verify_project_name!(project=nil, opts={})
|
27
|
+
project ||= project_name
|
28
|
+
tm = TagManager.new(:s3 => s3, :bucket => aws_bucket)
|
29
|
+
return if tm.projects.include?(project)
|
30
|
+
raise error_class, "Project name could not be determined" unless project
|
31
|
+
end
|
32
|
+
|
33
|
+
def files
|
34
|
+
# If a block is provided, filter the files before mapping them
|
35
|
+
files = block_given? ? yield(bucket.files) : bucket.files
|
36
|
+
Hash[files.parallel_map_with_index do |file, i|
|
37
|
+
key = file.key.split('/', 2)
|
38
|
+
|
39
|
+
file.attributes.merge!({
|
40
|
+
:index => i,
|
41
|
+
:name => key.last,
|
42
|
+
:project => key.first,
|
43
|
+
:age => (Time.now.to_f - file.last_modified.to_f),
|
44
|
+
:pretty_age => format_age(file.last_modified),
|
45
|
+
:pretty_length => format_size(file.content_length)
|
46
|
+
})
|
47
|
+
|
48
|
+
[file.key, file]
|
49
|
+
end]
|
50
|
+
end
|
51
|
+
|
52
|
+
def slugs(project)
|
53
|
+
filter = Proc.new do |files|
|
54
|
+
result=[]
|
55
|
+
# Fog only #maps against the first 1000 items, so we will use #each instead
|
56
|
+
files.each { |file| result << file if file.key =~ /^#{project}\/.*\.slug$/ } # ex match: project/blag.slug
|
57
|
+
result
|
58
|
+
end
|
59
|
+
files(&filter)
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_latest_slug
|
63
|
+
self.slugs(project_name).values.sort_by { |s| s.last_modified }.last
|
64
|
+
end
|
65
|
+
|
66
|
+
# finds a slug with name_part somewhere in the name. Use enough of the name to make
|
67
|
+
# it unique or this will just return the first slug it finds
|
68
|
+
def find_slug(name_part)
|
69
|
+
s = self.slugs(project_name).values.find_all { |f| f.attributes[:key].include?(name_part) }
|
70
|
+
if s.size == 0
|
71
|
+
raise error_class, "unable to find a slug from '#{name_part}'. Use 'wrangler list' command to see available slugs"
|
72
|
+
elsif s.size > 1
|
73
|
+
raise error_class, "ambiguous slug name. Found more than one slug with '#{name_part}' in their names.\n#{s.map{|sl| File.basename(sl.key)} * "\n"}\n Use 'wrangler list' command to see available slugs"
|
74
|
+
end
|
75
|
+
s[0]
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_slug_name(pattern)
|
79
|
+
slugs = self.slugs(project_name).values.select { |f| f.attributes[:key] =~ pattern }
|
80
|
+
return nil if slugs.empty?
|
81
|
+
slugs.sort_by { |s| s.last_modified }.last.key
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|