bugsnag-capistrano 1.1.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE/A.md +14 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +16 -0
- data/.github/support.md +22 -0
- data/.github/workflows/tests.yml +34 -0
- data/CHANGELOG.md +25 -0
- data/CONTRIBUTING.md +2 -1
- data/Gemfile +6 -3
- data/README.md +1 -3
- data/Rakefile +1 -19
- data/UPGRADING.md +27 -0
- data/VERSION +1 -1
- data/lib/bugsnag-capistrano/capistrano.rb +1 -1
- data/lib/bugsnag-capistrano/capistrano2.rb +14 -11
- data/lib/bugsnag-capistrano/release.rb +85 -0
- data/lib/bugsnag-capistrano/tasks/bugsnag.cap +13 -17
- data/spec/capistrano_spec.rb +93 -42
- data/spec/helpers/capistrano.rb +71 -0
- data/spec/helpers/server.rb +63 -0
- data/spec/release_spec.rb +94 -0
- data/spec/spec_helper.rb +2 -0
- metadata +15 -10
- data/.travis.yml +0 -77
- data/lib/bugsnag-capistrano/deploy.rb +0 -98
- data/lib/bugsnag-capistrano/tasks.rb +0 -2
- data/lib/bugsnag-capistrano/tasks/bugsnag-capistrano.rake +0 -85
- data/spec/deploy_spec.rb +0 -66
- data/spec/rake_spec.rb +0 -64
@@ -1,46 +1,42 @@
|
|
1
1
|
namespace :load do
|
2
|
-
|
3
2
|
task :defaults do
|
4
|
-
|
5
3
|
set :bugsnag_default_hooks, ->{ true }
|
6
|
-
|
7
4
|
end
|
8
|
-
|
9
5
|
end
|
10
6
|
|
11
7
|
namespace :deploy do
|
12
|
-
|
13
8
|
before :starting, :bugsnag_hooks do
|
14
9
|
invoke 'bugsnag:add_default_hooks' if fetch(:bugsnag_default_hooks)
|
15
10
|
end
|
16
|
-
|
17
11
|
end
|
18
12
|
|
19
13
|
namespace :bugsnag do
|
20
|
-
|
21
14
|
task :add_default_hooks do
|
22
|
-
after 'deploy:published', 'bugsnag:
|
15
|
+
after 'deploy:published', 'bugsnag:release'
|
23
16
|
end
|
24
17
|
|
25
|
-
desc 'Notify Bugsnag that new production code has been
|
26
|
-
task :
|
18
|
+
desc 'Notify Bugsnag that new production code has been released'
|
19
|
+
task :release do
|
27
20
|
run_locally do
|
28
21
|
begin
|
29
|
-
|
22
|
+
auto_assign_release_bool = ENV["BUGSNAG_AUTO_ASSIGN_RELEASE"] == 'true'
|
23
|
+
Bugsnag::Capistrano::Release.notify({
|
30
24
|
:api_key => fetch(:bugsnag_api_key, ENV["BUGSNAG_API_KEY"]),
|
25
|
+
:app_version => fetch(:app_version, ENV["BUGSNAG_APP_VERSION"]),
|
26
|
+
:auto_assign_release => fetch(:bugsnag_auto_assign_release, auto_assign_release_bool),
|
27
|
+
:builder_name => fetch(:bugsnag_builder, ENV["BUGSNAG_BUILDER_NAME"] || ENV["USER"]),
|
28
|
+
:metadata => fetch(:bugsnag_metadata),
|
31
29
|
:release_stage => fetch(:bugsnag_env) || ENV["BUGSNAG_RELEASE_STAGE"] || fetch(:rails_env) || fetch(:stage) || "production",
|
32
30
|
:revision => fetch(:current_revision, ENV["BUGSNAG_REVISION"]),
|
33
|
-
:repository => fetch(:repo_url, ENV["BUGSNAG_REPOSITORY"]),
|
34
|
-
:
|
35
|
-
:app_version => fetch(:app_version, ENV["BUGSNAG_APP_VERSION"]),
|
31
|
+
:repository => fetch(:bugsnag_repo_url, fetch(:repo_url, ENV["BUGSNAG_REPOSITORY"])),
|
32
|
+
:source_control_provider => fetch(:bugsnag_source_control_provider, ENV["BUGSNAG_SOURCE_CONTROL_PROVIDER"]),
|
36
33
|
:endpoint => fetch(:bugsnag_endpoint)
|
37
34
|
})
|
38
|
-
info 'Bugsnag
|
35
|
+
info 'Bugsnag release notification complete.'
|
39
36
|
rescue
|
40
|
-
error "Bugsnag
|
37
|
+
error "Bugsnag release notification failed, #{$!.inspect}"
|
41
38
|
end
|
42
39
|
end
|
43
40
|
end
|
44
|
-
|
45
41
|
end
|
46
42
|
# vi:ft=ruby
|
data/spec/capistrano_spec.rb
CHANGED
@@ -1,65 +1,116 @@
|
|
1
|
-
|
2
|
-
require 'rspec/expectations'
|
3
|
-
require 'rspec/mocks'
|
4
|
-
|
5
|
-
require 'webrick'
|
6
|
-
|
7
|
-
describe "bugsnag capistrano", :always do
|
8
|
-
|
9
|
-
server = nil
|
10
|
-
queue = Queue.new
|
11
|
-
cap_2 = ENV['CAP_2_TEST'] == 'true'
|
12
|
-
fixture_path = cap_2 ? '../examples/capistrano2' : '../examples/capistrano3'
|
13
|
-
exec_string = cap_2 ? 'bundle exec cap deploy' : 'bundle exec cap test deploy'
|
14
|
-
example_path = File.join(File.dirname(__FILE__), fixture_path)
|
15
|
-
|
16
|
-
before do
|
17
|
-
server = WEBrick::HTTPServer.new :Port => 0, :Logger => WEBrick::Log.new(STDOUT), :AccessLog => []
|
18
|
-
server.mount_proc '/deploy' do |req, res|
|
19
|
-
queue.push req.body
|
20
|
-
res.status = 200
|
21
|
-
res.body = "OK\n"
|
22
|
-
end
|
23
|
-
Thread.new{ server.start }
|
24
|
-
end
|
1
|
+
require_relative './spec_helper'
|
25
2
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
3
|
+
describe "bugsnag capistrano" do
|
4
|
+
server = Helpers::Server.new
|
5
|
+
|
6
|
+
before { server.start }
|
7
|
+
after { server.stop }
|
30
8
|
|
31
|
-
let(:request) { JSON.parse(queue.pop) }
|
32
|
-
|
33
9
|
it "sends a deploy notification to the set endpoint" do
|
34
|
-
ENV['BUGSNAG_ENDPOINT'] =
|
35
|
-
|
36
|
-
|
37
|
-
|
10
|
+
ENV['BUGSNAG_ENDPOINT'] = server.url
|
11
|
+
ENV['BUGSNAG_APP_VERSION'] = "1"
|
12
|
+
|
13
|
+
Dir.chdir(Helpers::Capistrano.example_path) do
|
14
|
+
system(Helpers::Capistrano.deploy_command)
|
38
15
|
end
|
39
16
|
|
40
|
-
payload =
|
17
|
+
payload = server.last_request
|
41
18
|
expect(payload["apiKey"]).to eq('YOUR_API_KEY')
|
19
|
+
expect(payload["appVersion"]).to eq("1")
|
42
20
|
expect(payload["releaseStage"]).to eq('production')
|
43
21
|
end
|
44
22
|
|
45
23
|
it "allows modifications of deployment characteristics" do
|
46
|
-
ENV['BUGSNAG_ENDPOINT'] =
|
24
|
+
ENV['BUGSNAG_ENDPOINT'] = server.url
|
47
25
|
ENV['BUGSNAG_API_KEY'] = "this is a test key"
|
48
26
|
ENV['BUGSNAG_RELEASE_STAGE'] = "test"
|
49
27
|
ENV['BUGSNAG_REVISION'] = "test"
|
50
28
|
ENV['BUGSNAG_APP_VERSION'] = "1"
|
51
29
|
ENV['BUGSNAG_REPOSITORY'] = "test@repo.com:test/test_repo.git"
|
30
|
+
ENV['BUGSNAG_SOURCE_CONTROL_PROVIDER'] = "github"
|
31
|
+
ENV['BUGSNAG_AUTO_ASSIGN_RELEASE'] = 'true'
|
52
32
|
|
53
|
-
Dir.chdir(example_path) do
|
54
|
-
system(
|
33
|
+
Dir.chdir(Helpers::Capistrano.example_path) do
|
34
|
+
system(Helpers::Capistrano.deploy_command)
|
55
35
|
end
|
56
36
|
|
57
|
-
payload =
|
37
|
+
payload = server.last_request
|
58
38
|
expect(payload["apiKey"]).to eq('this is a test key')
|
59
39
|
expect(payload["releaseStage"]).to eq('test')
|
60
|
-
expect(payload["repository"]).to eq("test@repo.com:test/test_repo.git")
|
61
40
|
expect(payload["appVersion"]).to eq("1")
|
62
|
-
expect(payload["
|
41
|
+
expect(payload["sourceControl"]).to_not be_nil
|
42
|
+
expect(payload["sourceControl"]["revision"]).to eq("test")
|
43
|
+
expect(payload["sourceControl"]["repository"]).to eq("test@repo.com:test/test_repo.git")
|
44
|
+
expect(payload["sourceControl"]["provider"]).to eq("github")
|
45
|
+
expect(payload["autoAssignRelease"]).to eq(true)
|
63
46
|
end
|
64
|
-
end
|
65
47
|
|
48
|
+
it "uses 'repo_url' in preference to 'BUGSNAG_REPOSITORY'" do
|
49
|
+
ENV['BUGSNAG_REPOSITORY'] = "unused@repo.com:unused/unused_repo.git"
|
50
|
+
|
51
|
+
capfile = Helpers::Capistrano.generate_capfile({
|
52
|
+
bugsnag_api_key: "this is a test key",
|
53
|
+
app_version: "1",
|
54
|
+
bugsnag_auto_assign_release: true,
|
55
|
+
bugsnag_builder: "bob",
|
56
|
+
bugsnag_metadata: { a: 1, b: 2 },
|
57
|
+
bugsnag_env: "test",
|
58
|
+
current_revision: "test1234",
|
59
|
+
repo_url: "test@repo.com:test/test_repo.git",
|
60
|
+
bugsnag_source_control_provider: "github",
|
61
|
+
bugsnag_endpoint: server.url,
|
62
|
+
})
|
63
|
+
|
64
|
+
Helpers::Capistrano.run(capfile)
|
65
|
+
|
66
|
+
payload = server.last_request
|
67
|
+
|
68
|
+
expect(payload["apiKey"]).to eq('this is a test key')
|
69
|
+
expect(payload["appVersion"]).to eq("1")
|
70
|
+
expect(payload["autoAssignRelease"]).to eq(true)
|
71
|
+
expect(payload["builderName"]).to eq("bob")
|
72
|
+
expect(payload["buildTool"]).to eq("bugsnag-capistrano")
|
73
|
+
expect(payload["metadata"]).to eq({ "a" => 1, "b" => 2 })
|
74
|
+
expect(payload["releaseStage"]).to eq("test")
|
75
|
+
expect(payload["sourceControl"]).to eq({
|
76
|
+
"revision" => "test1234",
|
77
|
+
"repository" => "test@repo.com:test/test_repo.git",
|
78
|
+
"provider" => "github",
|
79
|
+
})
|
80
|
+
end
|
81
|
+
|
82
|
+
it "uses 'bugsnag_repo_url' in preference to 'repo_url'" do
|
83
|
+
ENV['BUGSNAG_REPOSITORY'] = "unused@repo.com:unused/unused_repo.git"
|
84
|
+
|
85
|
+
capfile = Helpers::Capistrano.generate_capfile({
|
86
|
+
bugsnag_api_key: "this is a test key",
|
87
|
+
app_version: "1",
|
88
|
+
bugsnag_auto_assign_release: true,
|
89
|
+
bugsnag_builder: "bob",
|
90
|
+
bugsnag_metadata: { a: 1, b: 2 },
|
91
|
+
bugsnag_env: "test",
|
92
|
+
current_revision: "test1234",
|
93
|
+
repo_url: "test@repo.com:test/test_repo.git",
|
94
|
+
bugsnag_repo_url: "https://repo.com/test/test_repo.git",
|
95
|
+
bugsnag_source_control_provider: "github",
|
96
|
+
bugsnag_endpoint: server.url,
|
97
|
+
})
|
98
|
+
|
99
|
+
Helpers::Capistrano.run(capfile)
|
100
|
+
|
101
|
+
payload = server.last_request
|
102
|
+
|
103
|
+
expect(payload["apiKey"]).to eq('this is a test key')
|
104
|
+
expect(payload["appVersion"]).to eq("1")
|
105
|
+
expect(payload["autoAssignRelease"]).to eq(true)
|
106
|
+
expect(payload["builderName"]).to eq("bob")
|
107
|
+
expect(payload["buildTool"]).to eq("bugsnag-capistrano")
|
108
|
+
expect(payload["metadata"]).to eq({ "a" => 1, "b" => 2 })
|
109
|
+
expect(payload["releaseStage"]).to eq("test")
|
110
|
+
expect(payload["sourceControl"]).to eq({
|
111
|
+
"revision" => "test1234",
|
112
|
+
"repository" => "https://repo.com/test/test_repo.git",
|
113
|
+
"provider" => "github",
|
114
|
+
})
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Helpers
|
5
|
+
class Capistrano
|
6
|
+
def self.version_2?
|
7
|
+
ENV['CAP_2_TEST'] == 'true'
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.deploy_command
|
11
|
+
return 'bundle exec cap deploy' if version_2?
|
12
|
+
|
13
|
+
'bundle exec cap test deploy'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.example_path
|
17
|
+
fixture_path = version_2? ? '../../examples/capistrano2' : '../../examples/capistrano3'
|
18
|
+
|
19
|
+
File.join(File.dirname(__FILE__), fixture_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.generate_capfile(variables)
|
23
|
+
return generate_v2_capfile(variables) if version_2?
|
24
|
+
|
25
|
+
capfile = <<-RUBY.gsub(/^\s+/, "")
|
26
|
+
require "capistrano/setup"
|
27
|
+
|
28
|
+
require "capistrano/deploy"
|
29
|
+
require "capistrano/scm/git"
|
30
|
+
install_plugin Capistrano::SCM::Git
|
31
|
+
|
32
|
+
require "bugsnag-capistrano"
|
33
|
+
RUBY
|
34
|
+
|
35
|
+
# add calls to set each variable - "set(:key, value)"
|
36
|
+
variables.each do |key, value|
|
37
|
+
capfile << "set(:#{key}, #{value.inspect})\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
capfile
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.generate_v2_capfile(variables)
|
44
|
+
capfile = "require 'bugsnag-capistrano'\n"
|
45
|
+
|
46
|
+
# add calls to set each variable - "set(:key, value)"
|
47
|
+
variables.each do |key, value|
|
48
|
+
capfile << "set(:#{key}, #{value.inspect})\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
# add an empty deploy task
|
52
|
+
capfile << "task :deploy do\nend"
|
53
|
+
|
54
|
+
capfile
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.run(capfile)
|
58
|
+
Dir.mktmpdir do |path|
|
59
|
+
FileUtils.cp_r("#{example_path}/.", path)
|
60
|
+
|
61
|
+
File.open("#{path}/Capfile", "w") do |file|
|
62
|
+
file.write(capfile)
|
63
|
+
end
|
64
|
+
|
65
|
+
Dir.chdir(path) do
|
66
|
+
system(deploy_command)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'webrick'
|
3
|
+
|
4
|
+
module Helpers
|
5
|
+
class Server
|
6
|
+
def initialize
|
7
|
+
@queue = Queue.new
|
8
|
+
@server = nil
|
9
|
+
@thread = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
@server = WEBrick::HTTPServer.new({
|
14
|
+
Port: 0,
|
15
|
+
Logger: WEBrick::Log.new(STDOUT),
|
16
|
+
AccessLog: []
|
17
|
+
})
|
18
|
+
|
19
|
+
@server.mount_proc('/deploy') do |req, res|
|
20
|
+
@queue.push(req.body)
|
21
|
+
|
22
|
+
res.status = 200
|
23
|
+
res.body = "OK\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
@thread = Thread.new { @server.start }
|
27
|
+
|
28
|
+
loop do
|
29
|
+
break if @server.status == :Running
|
30
|
+
|
31
|
+
sleep(0.1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def stop
|
36
|
+
@queue.clear
|
37
|
+
@server.stop
|
38
|
+
@thread.join(5)
|
39
|
+
@server = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def url
|
43
|
+
raise "Server is not running!" if @server.nil?
|
44
|
+
|
45
|
+
"http://localhost:" + @server.config[:Port].to_s + "/deploy"
|
46
|
+
end
|
47
|
+
|
48
|
+
def last_request
|
49
|
+
retries = 0
|
50
|
+
|
51
|
+
begin
|
52
|
+
JSON.parse(@queue.pop(true))
|
53
|
+
rescue ThreadError
|
54
|
+
raise if retries >= 10
|
55
|
+
|
56
|
+
retries += 1
|
57
|
+
sleep(0.1)
|
58
|
+
|
59
|
+
retry
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
|
3
|
+
require 'rspec/expectations'
|
4
|
+
require 'rspec/mocks'
|
5
|
+
require 'webmock/rspec'
|
6
|
+
|
7
|
+
require 'bugsnag-capistrano/release'
|
8
|
+
|
9
|
+
module Bugsnag::Capistrano
|
10
|
+
class Release
|
11
|
+
attr_accessor :logger
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Bugsnag::Capistrano::Release do
|
16
|
+
it "should call notify_without bugsnag" do
|
17
|
+
expect(Bugsnag::Capistrano::Release).to receive(:deliver)
|
18
|
+
Bugsnag::Capistrano::Release.notify({:api_key => "test", :app_version => "1"})
|
19
|
+
end
|
20
|
+
|
21
|
+
it "delivers a request to the given url" do
|
22
|
+
url = "http://localhost:56456"
|
23
|
+
stub_request(:post, url)
|
24
|
+
.to_return(status:200, body: "")
|
25
|
+
Bugsnag::Capistrano::Release.deliver(url, nil)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "delivers a body unmodified" do
|
29
|
+
body = ::JSON.dump({
|
30
|
+
"paramA" => 'a',
|
31
|
+
"paramB" => 'b',
|
32
|
+
"paramHash" => {
|
33
|
+
"one" => 1,
|
34
|
+
"two" => 2,
|
35
|
+
"three" => 3
|
36
|
+
}
|
37
|
+
})
|
38
|
+
url = "http://localhost:56456"
|
39
|
+
request = stub_request(:post, url)
|
40
|
+
.with(body: body, headers: { 'Content-Type' => 'application/json'})
|
41
|
+
.to_return(status:200, body: "")
|
42
|
+
Bugsnag::Capistrano::Release.deliver(url, body)
|
43
|
+
assert_requested request
|
44
|
+
end
|
45
|
+
|
46
|
+
it "cannot send without an apikey" do
|
47
|
+
expect(Bugsnag::Capistrano::Release).to_not receive(:deliver)
|
48
|
+
expect(Bugsnag::Capistrano::Release.logger).to receive(:warn).with("Cannot deliver notification. Missing required apiKey")
|
49
|
+
|
50
|
+
Bugsnag::Capistrano::Release.notify()
|
51
|
+
end
|
52
|
+
|
53
|
+
it "cannot send without an appVersion" do
|
54
|
+
expect(Bugsnag::Capistrano::Release).to_not receive(:deliver)
|
55
|
+
expect(Bugsnag::Capistrano::Release.logger).to receive(:warn).with("Cannot deliver notification. Missing required appVersion")
|
56
|
+
|
57
|
+
Bugsnag::Capistrano::Release.notify({:api_key => "test"})
|
58
|
+
end
|
59
|
+
|
60
|
+
context "with bugsnag installed" do
|
61
|
+
before do
|
62
|
+
module Kernel
|
63
|
+
alias_method :old_require, :require
|
64
|
+
def require(path)
|
65
|
+
old_require(path) unless /^bugsnag/.match(path)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "gets information from bugsnag configuration if available" do
|
71
|
+
config = double
|
72
|
+
allow(config).to receive(:api_key).and_return("bugsnag_api_key")
|
73
|
+
allow(config).to receive(:app_version).and_return("bugsnag_app_version")
|
74
|
+
allow(config).to receive(:release_stage).and_return("bugsnag_release_stage")
|
75
|
+
|
76
|
+
allow(Bugsnag).to receive(:configuration).and_return(config)
|
77
|
+
expect(Bugsnag::Capistrano::Release).to receive(:deliver) do |uri, body_string|
|
78
|
+
expect(uri).to eq(Bugsnag::Capistrano::Release::DEFAULT_BUILD_ENDPOINT)
|
79
|
+
body = ::JSON.parse(body_string)
|
80
|
+
expect(body["apiKey"]).to eq("bugsnag_api_key")
|
81
|
+
expect(body["appVersion"]).to eq("bugsnag_app_version")
|
82
|
+
expect(body["releaseStage"]).to eq("bugsnag_release_stage")
|
83
|
+
end
|
84
|
+
|
85
|
+
Bugsnag::Capistrano::Release.notify()
|
86
|
+
end
|
87
|
+
|
88
|
+
after do
|
89
|
+
module Kernel
|
90
|
+
alias_method :require, :old_require
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|