metaforce 0.3.5 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +13 -32
- data/Rakefile +12 -0
- data/lib/metaforce/config.rb +12 -2
- data/lib/metaforce/error.rb +3 -0
- data/lib/metaforce/metadata/client.rb +30 -31
- data/lib/metaforce/metadata/transaction.rb +5 -4
- data/lib/metaforce/rake/deploy.rb +35 -0
- data/lib/metaforce/rake/retrieve.rb +39 -0
- data/lib/metaforce/rake/tests.rb +62 -0
- data/lib/metaforce/rake.rb +42 -0
- data/lib/metaforce/tasks/README.md +8 -3
- data/lib/metaforce/tasks/metaforce.rake +4 -44
- data/lib/metaforce/version.rb +1 -1
- data/lib/metaforce.rb +1 -0
- data/metaforce.gemspec +1 -0
- data/spec/fixtures/sample/Rakefile +2 -8
- data/spec/lib/metadata/metadata_spec.rb +5 -2
- data/spec/lib/metadata/transaction_spec.rb +5 -10
- metadata +32 -19
- data/lib/metaforce/dsl.rb +0 -42
- data/spec/lib/dsl_spec.rb +0 -93
data/README.md
CHANGED
@@ -16,46 +16,24 @@ client = Metaforce::Metadata::Client.new :username => 'username',
|
|
16
16
|
:password => 'password',
|
17
17
|
:security_token => 'security token')
|
18
18
|
|
19
|
+
# Describe the metadata on the organization
|
19
20
|
client.describe
|
20
21
|
# => { :metadata_objects => [{ :child_xml_names => "CustomLabel", :directory_name => "labels" ... }
|
21
22
|
|
23
|
+
# List all custom objects
|
22
24
|
client.list(:type => "CustomObject")
|
23
25
|
# => [{ :created_by_id => "005U0000000EGpcIAG", :created_by_name => "Eric Holmes", ... }]
|
24
26
|
|
25
|
-
|
27
|
+
# Deploy metadata to the organization
|
28
|
+
deployment = client.deploy(File.expand_path('path/to/src'))
|
26
29
|
# => #<Metaforce::Transaction:0x00000102779bf8 @id="04sU0000000WNWoIAO" @type=:deploy>
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
deployment.result(:wait_until_done => true)
|
31
|
+
# Get the result
|
32
|
+
deployment.result
|
32
33
|
# => { :id => "04sU0000000WNWoIAO", :messages => [{ :changed => true ... :success => true }
|
33
|
-
```
|
34
|
-
|
35
|
-
## DSL
|
36
|
-
Metaforce includes a lightweight DSL to make deployments and retrieve's easier.
|
37
|
-
|
38
|
-
```ruby
|
39
|
-
require "metaforce/dsl"
|
40
|
-
include Metaforce::DSL::Metadata
|
41
|
-
|
42
|
-
login :username => 'username', :password => 'password', :security_token => 'security token' do
|
43
34
|
|
44
|
-
|
45
|
-
|
46
|
-
puts result
|
47
|
-
end
|
48
|
-
|
49
|
-
retrieve File.expand_path("../src/package.xml", __FILE__) |result, zip|
|
50
|
-
puts "Successful retrieve!"
|
51
|
-
puts result
|
52
|
-
File.open("retrieve.zip", "wb") do |file|
|
53
|
-
file.write(zip)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
retrieve File.expand_path("../src/package.xml", __FILE__), :to => "directory"
|
58
|
-
end
|
35
|
+
# Retrieve the metadata components specified in package.xml and unzip to the "retrieved" directory
|
36
|
+
client.retrieve(File.expand_path('path/to/package.xml')).to('retrieved')
|
59
37
|
```
|
60
38
|
|
61
39
|
## Roadmap
|
@@ -67,8 +45,6 @@ need to be done.
|
|
67
45
|
* Implement CRUD based calls <http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_crud_based_calls_intro.htm>.
|
68
46
|
* Implement some helper methods for diffing metadata.
|
69
47
|
* Ability to deploy directly from a git repository.
|
70
|
-
* <del>Implement .retrieve for retrieving metadata.</del>
|
71
|
-
* <del>Implement a DSL.</del>
|
72
48
|
* And some other stuff that I haven't quite thought of yet...
|
73
49
|
|
74
50
|
## Contributing
|
@@ -77,6 +53,11 @@ feature on a new branch, then send me a pull request with a detailed
|
|
77
53
|
description. Please provide applicable rspec specs.
|
78
54
|
|
79
55
|
## Version History
|
56
|
+
**0.4.0** (March 2, 2012)
|
57
|
+
|
58
|
+
* Various bug fixes and improvements.
|
59
|
+
* Removed DSL to focus on core functionality.
|
60
|
+
|
80
61
|
**0.3.5** (February 11, 2012)
|
81
62
|
|
82
63
|
* Allow rake tasks to get credentials from a metaforce.yml file.
|
data/Rakefile
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
|
3
|
+
task :default => :spec
|
4
|
+
|
5
|
+
desc "Run rspec specs"
|
6
|
+
task :spec do
|
7
|
+
sh "bundle exec rspec spec"
|
8
|
+
end
|
9
|
+
|
3
10
|
desc "Start an irb session"
|
4
11
|
task :console do
|
5
12
|
sh "irb -I lib -r metaforce"
|
6
13
|
end
|
14
|
+
|
15
|
+
desc "Build and publish gem on rubygems.org"
|
16
|
+
task :publish do
|
17
|
+
sh "gem build metaforce.gemspec && gem push metaforce-*.gem"
|
18
|
+
end
|
data/lib/metaforce/config.rb
CHANGED
@@ -43,7 +43,17 @@ module Metaforce
|
|
43
43
|
# Defaults to false.
|
44
44
|
attr_accessor :test
|
45
45
|
# Causes Metaforce::Transaction.result to loop until the transaction is
|
46
|
-
# complete. Defaults to
|
46
|
+
# complete. Defaults to true.
|
47
|
+
#
|
48
|
+
# If you want to do custom polling, set this to false.
|
49
|
+
#
|
50
|
+
# == Example
|
51
|
+
#
|
52
|
+
# Metaforce.configuration.wait_until_done = false;
|
53
|
+
#
|
54
|
+
# deploy = client.deploy File.expand_path("myeclipseproj")
|
55
|
+
# begin; while !deploy.done?
|
56
|
+
# deploy.result
|
47
57
|
attr_accessor :wait_until_done
|
48
58
|
|
49
59
|
def initialize
|
@@ -51,7 +61,7 @@ module Metaforce
|
|
51
61
|
HTTPI.log = false
|
52
62
|
@api_version = "23.0"
|
53
63
|
@test = false
|
54
|
-
@wait_until_done =
|
64
|
+
@wait_until_done = true
|
55
65
|
end
|
56
66
|
|
57
67
|
def logger
|
@@ -2,13 +2,11 @@ require 'metaforce/manifest'
|
|
2
2
|
require 'savon'
|
3
3
|
require 'zip/zip'
|
4
4
|
require 'base64'
|
5
|
-
require '
|
5
|
+
require 'tmpdir'
|
6
6
|
|
7
7
|
module Metaforce
|
8
8
|
module Metadata
|
9
9
|
class Client
|
10
|
-
DEPLOY_ZIP = 'deploy.zip' # :nodoc:
|
11
|
-
RETRIEVE_ZIP = 'retrieve.zip' # :nodoc:
|
12
10
|
|
13
11
|
# Performs a login and sets the session_id and metadata_server_url.
|
14
12
|
#
|
@@ -45,9 +43,7 @@ module Metaforce
|
|
45
43
|
# client.list([{ :type => "CustomObject" }, { :type => "ApexComponent" }])
|
46
44
|
# #=> ["ContractContactRole", "Solution", "Invoice_Statements__c", ... ]
|
47
45
|
def list(queries=[])
|
48
|
-
unless queries.is_a?(Array)
|
49
|
-
queries = [ queries ]
|
50
|
-
end
|
46
|
+
queries = [ queries ] unless queries.is_a?(Array)
|
51
47
|
response = @client.request(:list_metadata) do |soap|
|
52
48
|
soap.header = @header
|
53
49
|
soap.body = {
|
@@ -127,8 +123,12 @@ module Metaforce
|
|
127
123
|
self.status(id)[:done] || false
|
128
124
|
end
|
129
125
|
|
130
|
-
# Deploys +
|
126
|
+
# Deploys +path+ to the organisation. +path+ can either be a path to
|
127
|
+
# a directory or a path to a zip file.
|
128
|
+
#
|
129
|
+
# +options+ can contain any of the following keys:
|
131
130
|
#
|
131
|
+
# +options+:
|
132
132
|
# See http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_deploy.htm#deploy_options
|
133
133
|
# for a list of _deploy_options_. Options should be convereted from
|
134
134
|
# camelCase to an :underscored_symbol.
|
@@ -143,12 +143,11 @@ module Metaforce
|
|
143
143
|
#
|
144
144
|
# deploy.status[:state]
|
145
145
|
# #=> "Completed"
|
146
|
-
def deploy(
|
147
|
-
if
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
zip_contents = Base64.encode64(dir.read)
|
146
|
+
def deploy(path, options={})
|
147
|
+
if path.is_a?(String)
|
148
|
+
zip_contents = create_deploy_file(path)
|
149
|
+
elsif path.is_a?(File)
|
150
|
+
zip_contents = Base64.encode64(path.read)
|
152
151
|
end
|
153
152
|
|
154
153
|
Metaforce.log('Executing deploy')
|
@@ -157,7 +156,7 @@ module Metaforce
|
|
157
156
|
soap.header = @header
|
158
157
|
soap.body = {
|
159
158
|
:zip_file => zip_contents,
|
160
|
-
:deploy_options =>
|
159
|
+
:deploy_options => options[:options] || {}
|
161
160
|
}
|
162
161
|
end
|
163
162
|
Transaction.deployment self, response[:deploy_response][:result][:id]
|
@@ -167,56 +166,56 @@ module Metaforce
|
|
167
166
|
#
|
168
167
|
# See http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_retrieve_request.htm
|
169
168
|
# for a list of _retrieve_request_ options. Options should be convereted from
|
170
|
-
# camelCase to an :underscored_symbol.
|
171
|
-
|
169
|
+
# camelCase to an :underscored_symbol. _retrieve_request_ options should
|
170
|
+
# be specified under the +:options+ key in options.
|
171
|
+
def retrieve(options={})
|
172
172
|
Metaforce.log('Executing retrieve')
|
173
173
|
|
174
174
|
response = @client.request(:retrieve) do |soap|
|
175
175
|
soap.header = @header
|
176
176
|
soap.body = {
|
177
|
-
:retrieve_request =>
|
177
|
+
:retrieve_request => options[:options] || {}
|
178
178
|
}
|
179
179
|
end
|
180
180
|
Transaction.retrieval self, response[:retrieve_response][:result][:id]
|
181
181
|
end
|
182
182
|
|
183
|
-
# Retrieves files specified in the manifest file (package.xml). Specificy any extra
|
183
|
+
# Retrieves files specified in the manifest file (package.xml). Specificy any extra options in +options[:options]+.
|
184
184
|
#
|
185
185
|
# == Examples
|
186
186
|
#
|
187
187
|
# retrieve = client.retrieve_unpackaged File.expand_path("spec/fixtures/sample/src/package.xml")
|
188
188
|
# #=> #<Metaforce::Transaction:0x1159bd0 @id='04sU0000000Wx6KIAS' @type=:retrieve>
|
189
|
-
def retrieve_unpackaged(manifest,
|
189
|
+
def retrieve_unpackaged(manifest, options={})
|
190
190
|
if manifest.is_a?(Metaforce::Manifest)
|
191
191
|
package = manifest.to_package
|
192
192
|
elsif manifest.is_a?(String)
|
193
193
|
package = Metaforce::Manifest.new(File.open(manifest).read).to_package
|
194
194
|
end
|
195
|
-
|
195
|
+
options[:options] = {
|
196
196
|
:api_version => Metaforce.configuration.api_version,
|
197
197
|
:single_package => true,
|
198
198
|
:unpackaged => {
|
199
199
|
:types => package
|
200
200
|
}
|
201
|
-
}
|
202
|
-
|
203
|
-
retrieve(retrieve_request)
|
201
|
+
}.merge(options[:options] || {})
|
202
|
+
retrieve(options)
|
204
203
|
end
|
205
204
|
|
206
205
|
private
|
207
206
|
|
208
207
|
# Creates the deploy file, reads in the contents and returns the base64
|
209
208
|
# encoded data
|
210
|
-
def create_deploy_file(
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
209
|
+
def create_deploy_file(dir)
|
210
|
+
Dir.mktmpdir do |path|
|
211
|
+
path = File.join path, 'deploy.zip'
|
212
|
+
Zip::ZipFile.open(path, Zip::ZipFile::CREATE) do |zip|
|
213
|
+
Dir["#{dir}/**/**"].each do |file|
|
214
|
+
zip.add(file.sub("#{File.dirname(dir)}/", ''), file)
|
215
|
+
end
|
215
216
|
end
|
217
|
+
Base64.encode64(File.open(path, "rb").read)
|
216
218
|
end
|
217
|
-
contents = Base64.encode64(File.open(filename, "rb").read)
|
218
|
-
File.delete(filename)
|
219
|
-
contents
|
220
219
|
end
|
221
220
|
|
222
221
|
end
|
@@ -13,6 +13,7 @@ module Metaforce
|
|
13
13
|
@client = client
|
14
14
|
@id = id
|
15
15
|
@type = type
|
16
|
+
wait_until_done if Metaforce.configuration.wait_until_done
|
16
17
|
end
|
17
18
|
|
18
19
|
# Creates a new transaction and sets type to +:deploy+.
|
@@ -41,11 +42,11 @@ module Metaforce
|
|
41
42
|
# Returns the decoded content of the returned zip file.
|
42
43
|
def zip_file
|
43
44
|
raise 'Request was not a retrieve.' unless @type == :retrieve
|
44
|
-
Base64.decode64(
|
45
|
+
Base64.decode64(result[:zip_file])
|
45
46
|
end
|
46
47
|
|
47
48
|
# Unzips the returned zip file to +destination+.
|
48
|
-
def
|
49
|
+
def to(destination)
|
49
50
|
zip = zip_file
|
50
51
|
file = Tempfile.new('retrieve')
|
51
52
|
file.write(zip)
|
@@ -59,13 +60,13 @@ module Metaforce
|
|
59
60
|
zip.extract(f, path) { true }
|
60
61
|
end
|
61
62
|
end
|
63
|
+
self
|
62
64
|
end
|
63
65
|
|
64
66
|
# Returns the deploy or retrieve result
|
65
67
|
def result(options={})
|
66
|
-
self.wait_until_done if (options[:wait_until_done] || Metaforce.configuration.wait_until_done)
|
67
|
-
raise 'Request has not completed.' unless @done
|
68
68
|
@result = @client.status(@id, @type) if @result.nil?
|
69
|
+
raise SalesforceError, @result[:message] if @result[:state] == "Error"
|
69
70
|
@result
|
70
71
|
end
|
71
72
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'metaforce'
|
2
|
+
require 'term/ansicolor'
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/tasklib'
|
5
|
+
include Term::ANSIColor
|
6
|
+
|
7
|
+
module Metaforce
|
8
|
+
module Rake
|
9
|
+
|
10
|
+
class DeployTask < ::Rake::TaskLib
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
# The directory to deploy
|
14
|
+
attr_accessor :directory
|
15
|
+
|
16
|
+
def initialize(name = 'metaforce:deploy')
|
17
|
+
@name = name
|
18
|
+
@directory = File.expand_path('src')
|
19
|
+
yield self if block_given?
|
20
|
+
define
|
21
|
+
end
|
22
|
+
|
23
|
+
def define
|
24
|
+
desc "Deploy metadata"
|
25
|
+
task @name do
|
26
|
+
client = Metaforce::Rake.client
|
27
|
+
d = client.deploy(@directory)
|
28
|
+
puts green "Deployed #{@directory} successfully" if d.result[:success]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'metaforce'
|
2
|
+
require 'term/ansicolor'
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/tasklib'
|
5
|
+
include Term::ANSIColor
|
6
|
+
|
7
|
+
module Metaforce
|
8
|
+
module Rake
|
9
|
+
|
10
|
+
class RetrieveTask < ::Rake::TaskLib
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
# Path to the manifest file
|
14
|
+
attr_accessor :manifest
|
15
|
+
|
16
|
+
# The directory to unzip the retrieved files to
|
17
|
+
attr_accessor :directory
|
18
|
+
|
19
|
+
def initialize(name = 'metaforce:retrieve')
|
20
|
+
@name = name
|
21
|
+
@manifest = File.expand_path('src/package.xml')
|
22
|
+
@directory = File.expand_path('retrieved')
|
23
|
+
yield self if block_given?
|
24
|
+
define
|
25
|
+
end
|
26
|
+
|
27
|
+
def define
|
28
|
+
desc "Retrieve metadata"
|
29
|
+
task @name do
|
30
|
+
client = Metaforce::Rake.client
|
31
|
+
r = client.retrieve_unpackaged(@manifest).to(@directory)
|
32
|
+
puts green "Files retrieved sucessfully to #{@directory}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'metaforce'
|
2
|
+
require 'term/ansicolor'
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/tasklib'
|
5
|
+
include Term::ANSIColor
|
6
|
+
|
7
|
+
module Metaforce
|
8
|
+
module Rake
|
9
|
+
|
10
|
+
class TestsTask < ::Rake::TaskLib
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
# The directory to deploy
|
14
|
+
attr_accessor :directory
|
15
|
+
|
16
|
+
def initialize(name = 'metaforce:tests:ci')
|
17
|
+
@name = name
|
18
|
+
@directory = File.expand_path('src')
|
19
|
+
yield self if block_given?
|
20
|
+
define
|
21
|
+
end
|
22
|
+
|
23
|
+
def define
|
24
|
+
desc "Deploy metadata, run all tests, then undeploy"
|
25
|
+
task @name do
|
26
|
+
ENV['env'] = ENV['env'] || 'ci'
|
27
|
+
client = Metaforce::Rake.client
|
28
|
+
Metaforce.log = false
|
29
|
+
result = client.deploy(@directory, :options => { :run_all_tests => true }).result
|
30
|
+
failures = result[:run_test_result][:failures]
|
31
|
+
|
32
|
+
if failures
|
33
|
+
failures = [failures] unless failures.is_a?(Array)
|
34
|
+
puts "Failures:"
|
35
|
+
failures.each_with_index do |failure, index|
|
36
|
+
index = index + 1
|
37
|
+
puts
|
38
|
+
puts "\t#{index}) #{failure[:method_name]}"
|
39
|
+
puts red "\t #{failure[:message]}"
|
40
|
+
puts
|
41
|
+
puts magenta "\t ##{failure[:stack_trace]}"
|
42
|
+
puts
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
color = failures ? :red : :green
|
47
|
+
puts "Finished in #{Float(result[:run_test_result][:total_time]) / 100} seconds"
|
48
|
+
puts send(color, "#{result[:run_test_result][:num_tests_run]} tests, #{result[:run_test_result][:num_failures]} failures")
|
49
|
+
|
50
|
+
if failures
|
51
|
+
puts "\nFailed tests:\n"
|
52
|
+
failures.each do |failure|
|
53
|
+
puts "#{red failure[:stack_trace]} #{magenta "# #{failure[:method_name]}"}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
abort if failures
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'metaforce/rake/deploy'
|
3
|
+
require 'metaforce/rake/retrieve'
|
4
|
+
require 'metaforce/rake/tests'
|
5
|
+
|
6
|
+
module Metaforce
|
7
|
+
module Rake
|
8
|
+
|
9
|
+
class << self
|
10
|
+
CONFIG_FILE = 'metaforce.yml'
|
11
|
+
|
12
|
+
# Loads the config and creates a client
|
13
|
+
def client
|
14
|
+
load_config
|
15
|
+
Metaforce::Metadata::Client.new :username => @username,
|
16
|
+
:password => @password,
|
17
|
+
:security_token => @security_token
|
18
|
+
end
|
19
|
+
|
20
|
+
# Loads configuration settings from metaforce.yml
|
21
|
+
def load_config
|
22
|
+
if File.exists?(CONFIG_FILE)
|
23
|
+
config = YAML.load_file(CONFIG_FILE)
|
24
|
+
env = ENV['env'] || 'default'
|
25
|
+
config = config.has_key?(env) ? config[env] : config
|
26
|
+
@username = config["username"]
|
27
|
+
@password = config["password"]
|
28
|
+
@security_token = config["security_token"] || ''
|
29
|
+
@test = config["test"] || false
|
30
|
+
@log = config["log"] || true
|
31
|
+
Metaforce.log = @log
|
32
|
+
Metaforce.configuration.test = @test
|
33
|
+
else
|
34
|
+
print "username: "; @username = STDIN.gets.chomp
|
35
|
+
print "password: "; @password = STDIN.gets.chomp
|
36
|
+
print "security token: "; @security_token = STDIN.gets.chomp
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -4,6 +4,7 @@ metadata using Rake and Metaforce. The following rake tasks are provided:
|
|
4
4
|
|
5
5
|
* metaforce:deploy
|
6
6
|
* metaforce:retrieve
|
7
|
+
* metaforce:tests:ci
|
7
8
|
|
8
9
|
You can include the rake tasks by adding the following to your Rakefile:
|
9
10
|
|
@@ -51,7 +52,11 @@ This would deploy using the `sandbox` environment:
|
|
51
52
|
|
52
53
|
`rake metaforce:deploy env="sandbox"`
|
53
54
|
|
54
|
-
|
55
|
-
|
55
|
+
## Continuous Integration
|
56
|
+
The `metaforce:tests:ci` task is provided to make continuous integration with
|
57
|
+
something like [Jenkins](http://jenkins-ci.org) easier.
|
56
58
|
|
57
|
-
`
|
59
|
+
By default, the `metaforce:tests:ci` task will use the `ci` environment in
|
60
|
+
metaforce.yml, so you should set that up before you use this task.
|
61
|
+
|
62
|
+
The task will deploy the metadata and run all tests.
|
@@ -1,45 +1,5 @@
|
|
1
|
-
|
2
|
-
Metaforce.log = true
|
1
|
+
require 'metaforce/rake'
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
config = YAML.load_file('metaforce.yml')
|
8
|
-
env = ENV['env'] || 'default'
|
9
|
-
root = config.has_key?(env) ? config[env] : config
|
10
|
-
username = root["username"]
|
11
|
-
password = root["password"]
|
12
|
-
security_token = root["security_token"] || ''
|
13
|
-
test = root["test"] || false
|
14
|
-
Metaforce.configuration.test = test
|
15
|
-
else
|
16
|
-
print "username: "; username = STDIN.gets.chomp
|
17
|
-
print "password: "; password = STDIN.gets.chomp
|
18
|
-
print "security token: "; security_token = STDIN.gets.chomp
|
19
|
-
end
|
20
|
-
@client = Metaforce::Metadata::Client.new :username => username,
|
21
|
-
:password => password,
|
22
|
-
:security_token => security_token
|
23
|
-
end
|
24
|
-
|
25
|
-
desc "Deploy current directory to the organization"
|
26
|
-
task :deploy => :login do
|
27
|
-
deployment = @client.deploy File.dirname(__FILE__)
|
28
|
-
result = deployment.result(:wait_until_done => true)
|
29
|
-
if result[:success]
|
30
|
-
puts "Successfully deployed metadata."
|
31
|
-
else
|
32
|
-
puts "An error occurred."
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
desc "Retrieve metadata from the organization to src directory"
|
37
|
-
task :retrieve => :login do
|
38
|
-
dir = ENV['dir'] || 'src'
|
39
|
-
retrieval = @client.retrieve_unpackaged(File.expand_path('src/package.xml'))
|
40
|
-
result = retrieval.result(:wait_until_done => true)
|
41
|
-
retrieval.unzip(File.expand_path(dir))
|
42
|
-
puts "Successfully retrieved metadata to '#{dir}'."
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
3
|
+
Metaforce::Rake::DeployTask.new
|
4
|
+
Metaforce::Rake::RetrieveTask.new
|
5
|
+
Metaforce::Rake::TestsTask.new
|
data/lib/metaforce/version.rb
CHANGED
data/lib/metaforce.rb
CHANGED
data/metaforce.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_dependency "nokogiri", "~> 1.5.0"
|
22
22
|
s.add_dependency "savon", "~> 0.9.7"
|
23
23
|
s.add_dependency "rubyzip", "~> 0.9.5"
|
24
|
+
s.add_dependency "term-ansicolor"
|
24
25
|
|
25
26
|
s.add_development_dependency "rake"
|
26
27
|
s.add_development_dependency "rspec"
|
@@ -129,6 +129,7 @@ describe Metaforce::Metadata::Client do
|
|
129
129
|
|
130
130
|
it "deploys the directory and returns a transaction" do
|
131
131
|
savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
|
132
|
+
savon.expects(:check_status).with(:ids => ['04sU0000000WNWoIAO']).returns(:done);
|
132
133
|
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
133
134
|
deployment.should be_a(Metaforce::Transaction)
|
134
135
|
deployment.id.should eq("04sU0000000WNWoIAO")
|
@@ -138,8 +139,9 @@ describe Metaforce::Metadata::Client do
|
|
138
139
|
|
139
140
|
it "allows deploy options to be configured via a hash" do
|
140
141
|
savon.expects(:deploy).with(:zip_file => '', :deploy_options => { :run_all_tests => true }).returns(:in_progress)
|
142
|
+
savon.expects(:check_status).with(:ids => ['04sU0000000WNWoIAO']).returns(:done);
|
141
143
|
expect {
|
142
|
-
client.deploy('', { :run_all_tests => true })
|
144
|
+
client.deploy('', :options => { :run_all_tests => true })
|
143
145
|
}.to_not raise_error
|
144
146
|
end
|
145
147
|
|
@@ -188,11 +190,12 @@ describe Metaforce::Metadata::Client do
|
|
188
190
|
context "when given extra retrieve options" do
|
189
191
|
before(:each) do
|
190
192
|
savon.expects(:retrieve).with(:retrieve_request => { :api_version => Metaforce.configuration.api_version, :single_package => true, :unpackaged => { :types => manifest.to_package }, :extra => true }).returns(:in_progress)
|
193
|
+
savon.expects(:check_status).with(:ids => ['04sU0000000WkdIIAS']).returns(:done);
|
191
194
|
end
|
192
195
|
|
193
196
|
it "merges the options" do
|
194
197
|
expect {
|
195
|
-
retrieval = client.retrieve_unpackaged(File.expand_path('../../../fixtures/sample/src/package.xml', __FILE__), { :extra => true })
|
198
|
+
retrieval = client.retrieve_unpackaged(File.expand_path('../../../fixtures/sample/src/package.xml', __FILE__), :options => { :extra => true })
|
196
199
|
}.to_not raise_error
|
197
200
|
end
|
198
201
|
|
@@ -15,15 +15,15 @@ describe Metaforce::Transaction do
|
|
15
15
|
|
16
16
|
it "allows you to check if the transaction has completed" do
|
17
17
|
savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
|
18
|
-
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
19
18
|
savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
19
|
+
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
20
20
|
deployment.done?.should eq(true)
|
21
21
|
end
|
22
22
|
|
23
23
|
it "doesn't send a request if it has already completed" do
|
24
24
|
savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
|
25
|
-
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
26
25
|
savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
26
|
+
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
27
27
|
deployment.done?.should eq(true)
|
28
28
|
expect { deployment.done?.should eq(true) }.to_not raise_error
|
29
29
|
end
|
@@ -34,6 +34,7 @@ describe Metaforce::Transaction do
|
|
34
34
|
|
35
35
|
it "returns the status" do
|
36
36
|
savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
|
37
|
+
savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
37
38
|
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
38
39
|
savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
39
40
|
deployment.status.should be_a(Hash)
|
@@ -43,16 +44,10 @@ describe Metaforce::Transaction do
|
|
43
44
|
|
44
45
|
describe ".result" do
|
45
46
|
|
46
|
-
it "raises an error if .done? hasn't been called" do
|
47
|
-
savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
|
48
|
-
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
49
|
-
expect { deployment.result }.to raise_error
|
50
|
-
end
|
51
|
-
|
52
47
|
it "allows you to check the transaction result" do
|
53
48
|
savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
|
54
|
-
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
55
49
|
savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
50
|
+
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
56
51
|
deployment.done?.should eq(true)
|
57
52
|
savon.expects(:check_deploy_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
58
53
|
deployment.result.should be_a(Hash)
|
@@ -60,8 +55,8 @@ describe Metaforce::Transaction do
|
|
60
55
|
|
61
56
|
it "doesn't send a request if it has already retrieved the result" do
|
62
57
|
savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
|
63
|
-
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
64
58
|
savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
59
|
+
deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
|
65
60
|
deployment.done?.should eq(true)
|
66
61
|
savon.expects(:check_deploy_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
67
62
|
deployment.result.should be_a(Hash)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metaforce
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02
|
12
|
+
date: 2012-03-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
16
|
-
requirement: &
|
16
|
+
requirement: &2152167300 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.5.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2152167300
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: savon
|
27
|
-
requirement: &
|
27
|
+
requirement: &2152166720 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 0.9.7
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2152166720
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rubyzip
|
38
|
-
requirement: &
|
38
|
+
requirement: &2152164880 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,21 @@ dependencies:
|
|
43
43
|
version: 0.9.5
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2152164880
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: term-ansicolor
|
49
|
+
requirement: &2152180240 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2152180240
|
47
58
|
- !ruby/object:Gem::Dependency
|
48
59
|
name: rake
|
49
|
-
requirement: &
|
60
|
+
requirement: &2152176860 !ruby/object:Gem::Requirement
|
50
61
|
none: false
|
51
62
|
requirements:
|
52
63
|
- - ! '>='
|
@@ -54,10 +65,10 @@ dependencies:
|
|
54
65
|
version: '0'
|
55
66
|
type: :development
|
56
67
|
prerelease: false
|
57
|
-
version_requirements: *
|
68
|
+
version_requirements: *2152176860
|
58
69
|
- !ruby/object:Gem::Dependency
|
59
70
|
name: rspec
|
60
|
-
requirement: &
|
71
|
+
requirement: &2152175140 !ruby/object:Gem::Requirement
|
61
72
|
none: false
|
62
73
|
requirements:
|
63
74
|
- - ! '>='
|
@@ -65,10 +76,10 @@ dependencies:
|
|
65
76
|
version: '0'
|
66
77
|
type: :development
|
67
78
|
prerelease: false
|
68
|
-
version_requirements: *
|
79
|
+
version_requirements: *2152175140
|
69
80
|
- !ruby/object:Gem::Dependency
|
70
81
|
name: mocha
|
71
|
-
requirement: &
|
82
|
+
requirement: &2152188880 !ruby/object:Gem::Requirement
|
72
83
|
none: false
|
73
84
|
requirements:
|
74
85
|
- - ! '>='
|
@@ -76,10 +87,10 @@ dependencies:
|
|
76
87
|
version: '0'
|
77
88
|
type: :development
|
78
89
|
prerelease: false
|
79
|
-
version_requirements: *
|
90
|
+
version_requirements: *2152188880
|
80
91
|
- !ruby/object:Gem::Dependency
|
81
92
|
name: savon_spec
|
82
|
-
requirement: &
|
93
|
+
requirement: &2152186660 !ruby/object:Gem::Requirement
|
83
94
|
none: false
|
84
95
|
requirements:
|
85
96
|
- - ~>
|
@@ -87,7 +98,7 @@ dependencies:
|
|
87
98
|
version: 0.1.6
|
88
99
|
type: :development
|
89
100
|
prerelease: false
|
90
|
-
version_requirements: *
|
101
|
+
version_requirements: *2152186660
|
91
102
|
description: A Ruby gem for interacting with the Salesforce Metadata API
|
92
103
|
email:
|
93
104
|
- eric@ejholmes.net
|
@@ -103,11 +114,15 @@ files:
|
|
103
114
|
- Rakefile
|
104
115
|
- lib/metaforce.rb
|
105
116
|
- lib/metaforce/config.rb
|
106
|
-
- lib/metaforce/
|
117
|
+
- lib/metaforce/error.rb
|
107
118
|
- lib/metaforce/manifest.rb
|
108
119
|
- lib/metaforce/metadata.rb
|
109
120
|
- lib/metaforce/metadata/client.rb
|
110
121
|
- lib/metaforce/metadata/transaction.rb
|
122
|
+
- lib/metaforce/rake.rb
|
123
|
+
- lib/metaforce/rake/deploy.rb
|
124
|
+
- lib/metaforce/rake/retrieve.rb
|
125
|
+
- lib/metaforce/rake/tests.rb
|
111
126
|
- lib/metaforce/services.rb
|
112
127
|
- lib/metaforce/services/client.rb
|
113
128
|
- lib/metaforce/tasks/README.md
|
@@ -134,7 +149,6 @@ files:
|
|
134
149
|
- spec/fixtures/sample/src/classes/TestClass.cls-meta.xml
|
135
150
|
- spec/fixtures/sample/src/package.xml
|
136
151
|
- spec/lib/config_spec.rb
|
137
|
-
- spec/lib/dsl_spec.rb
|
138
152
|
- spec/lib/manifest_spec.rb
|
139
153
|
- spec/lib/metadata/metadata_spec.rb
|
140
154
|
- spec/lib/metadata/transaction_spec.rb
|
@@ -185,7 +199,6 @@ test_files:
|
|
185
199
|
- spec/fixtures/sample/src/classes/TestClass.cls-meta.xml
|
186
200
|
- spec/fixtures/sample/src/package.xml
|
187
201
|
- spec/lib/config_spec.rb
|
188
|
-
- spec/lib/dsl_spec.rb
|
189
202
|
- spec/lib/manifest_spec.rb
|
190
203
|
- spec/lib/metadata/metadata_spec.rb
|
191
204
|
- spec/lib/metadata/transaction_spec.rb
|
data/lib/metaforce/dsl.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'tempfile'
|
2
|
-
require 'zip/zip'
|
3
|
-
|
4
|
-
module Metaforce
|
5
|
-
module DSL
|
6
|
-
module Metadata
|
7
|
-
|
8
|
-
# Logs in and creates a new instance of Metaforce::Metadata::Client
|
9
|
-
def login(options)
|
10
|
-
@client = Metaforce::Metadata::Client.new options
|
11
|
-
yield if block_given?
|
12
|
-
end
|
13
|
-
|
14
|
-
# Deploy the contents of _dir_ to the target organization
|
15
|
-
def deploy(dir, options={})
|
16
|
-
deployment = @client.deploy(dir, options)
|
17
|
-
result = deployment.result(:wait_until_done => true)
|
18
|
-
raise "Deploy failed." if !result[:success]
|
19
|
-
yield result if block_given?
|
20
|
-
end
|
21
|
-
|
22
|
-
# Retrieve the metadata specified in the manifest file. If manifest is a
|
23
|
-
# Hash, the underlying .retrieve method is used instead of
|
24
|
-
# .retrieve_unpackaged.
|
25
|
-
#
|
26
|
-
# If _options_ # contains a key _:to_, the resulting zip
|
27
|
-
# file that is retrieved is unzipped to the directory specified.
|
28
|
-
def retrieve(manifest, options={})
|
29
|
-
if manifest.is_a?(Hash)
|
30
|
-
retrieval = @client.retrieve(manifest)
|
31
|
-
else
|
32
|
-
retrieval = @client.retrieve_unpackaged(manifest)
|
33
|
-
end
|
34
|
-
result = retrieval.result(:wait_until_done => true)
|
35
|
-
zip_contents = retrieval.zip_file
|
36
|
-
retrieval.unzip(options[:to]) if options.has_key?(:to)
|
37
|
-
yield result, zip_contents if block_given?
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/spec/lib/dsl_spec.rb
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "metaforce/dsl"
|
3
|
-
include Metaforce::DSL::Metadata
|
4
|
-
|
5
|
-
describe Metaforce::DSL::Metadata do
|
6
|
-
|
7
|
-
let(:manifest) do
|
8
|
-
Metaforce::Manifest.new(File.open(File.expand_path('../../fixtures/sample/src/package.xml', __FILE__)).read)
|
9
|
-
end
|
10
|
-
|
11
|
-
let(:valid_credentials) do
|
12
|
-
{ :username => 'valid', :password => 'password' }
|
13
|
-
end
|
14
|
-
|
15
|
-
describe ".login" do
|
16
|
-
|
17
|
-
before(:each) do
|
18
|
-
savon.expects(:login).with(valid_credentials).returns(:success)
|
19
|
-
end
|
20
|
-
|
21
|
-
it "logs the user in" do
|
22
|
-
expect { login valid_credentials }.to_not raise_error
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
describe ".deploy" do
|
28
|
-
|
29
|
-
before(:each) do
|
30
|
-
Metaforce::Metadata::Client.any_instance.stubs(:create_deploy_file).returns('')
|
31
|
-
savon.expects(:login).with(valid_credentials).returns(:success)
|
32
|
-
savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
|
33
|
-
savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
34
|
-
savon.expects(:check_deploy_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
|
35
|
-
end
|
36
|
-
|
37
|
-
context "when given a directory" do
|
38
|
-
|
39
|
-
it "deploys the directory" do
|
40
|
-
login valid_credentials
|
41
|
-
expect {
|
42
|
-
deploy File.expand_path('../../../fixtures/sample', __FILE__) do |result|
|
43
|
-
result.should be_a(Hash)
|
44
|
-
end
|
45
|
-
}.to_not raise_error
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
describe ".retrieve" do
|
53
|
-
before(:each) do
|
54
|
-
savon.expects(:login).with(valid_credentials).returns(:success)
|
55
|
-
savon.expects(:retrieve).with(:retrieve_request => { :api_version => Metaforce.configuration.api_version, :single_package => true, :unpackaged => { :types => manifest.to_package } }).returns(:in_progress)
|
56
|
-
savon.expects(:check_status).with(:ids => ['04sU0000000WkdIIAS']).returns(:done)
|
57
|
-
savon.expects(:check_retrieve_status).with(:ids => ['04sU0000000WkdIIAS']).returns(:success)
|
58
|
-
end
|
59
|
-
|
60
|
-
context "when given a manifest file" do
|
61
|
-
|
62
|
-
it "retrieves the components" do
|
63
|
-
login valid_credentials
|
64
|
-
expect {
|
65
|
-
retrieve manifest do |result, zip|
|
66
|
-
result.should be_a(Hash)
|
67
|
-
zip.should be_a(String)
|
68
|
-
end
|
69
|
-
}.to_not raise_error
|
70
|
-
end
|
71
|
-
|
72
|
-
context "and :to is specified" do
|
73
|
-
|
74
|
-
before(:each) do
|
75
|
-
Metaforce::Transaction.any_instance.stubs(:unzip).returns('')
|
76
|
-
end
|
77
|
-
|
78
|
-
it "retrieves the components and unzips them to the directory" do
|
79
|
-
login valid_credentials
|
80
|
-
expect {
|
81
|
-
retrieve manifest, :to => "test_retrieve" do |result, zip|
|
82
|
-
result.should be_a(Hash)
|
83
|
-
zip.should be_a(String)
|
84
|
-
end
|
85
|
-
}.to_not raise_error
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|
93
|
-
end
|