bard 1.4.8 → 1.4.9

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +22 -0
  3. data/Gemfile +4 -0
  4. data/bard.gemspec +3 -2
  5. data/lib/bard/git.rb +5 -1
  6. data/lib/bard/provision/repo.rb +21 -0
  7. data/lib/bard/provision/ssh.rb +21 -0
  8. data/lib/bard/server.rb +2 -2
  9. data/lib/bard/version.rb +1 -1
  10. data/spec/bard/cli/ci_spec.rb +139 -0
  11. data/spec/bard/cli/command_spec.rb +50 -0
  12. data/spec/bard/cli/data_spec.rb +77 -0
  13. data/spec/bard/cli/deploy_spec.rb +202 -0
  14. data/spec/bard/cli/hurt_spec.rb +23 -0
  15. data/spec/bard/cli/install_spec.rb +25 -0
  16. data/spec/bard/cli/master_key_spec.rb +52 -0
  17. data/spec/bard/cli/new_spec.rb +60 -0
  18. data/spec/bard/cli/open_spec.rb +67 -0
  19. data/spec/bard/cli/ping_spec.rb +47 -0
  20. data/spec/bard/cli/provision_spec.rb +42 -0
  21. data/spec/bard/cli/run_spec.rb +51 -0
  22. data/spec/bard/cli/setup_spec.rb +76 -0
  23. data/spec/bard/cli/ssh_spec.rb +55 -0
  24. data/spec/bard/cli/stage_spec.rb +77 -0
  25. data/spec/bard/cli/vim_spec.rb +34 -0
  26. data/spec/bard/command_spec.rb +37 -0
  27. data/spec/bard/copy_spec.rb +33 -0
  28. data/spec/bard/git_spec.rb +61 -0
  29. data/spec/bard/github_pages_spec.rb +80 -0
  30. data/spec/bard/github_spec.rb +45 -0
  31. data/spec/bard/ping_spec.rb +31 -0
  32. data/spec/bard/provision/app_spec.rb +33 -0
  33. data/spec/bard/provision/apt_spec.rb +39 -0
  34. data/spec/bard/provision/authorizedkeys_spec.rb +40 -0
  35. data/spec/bard/provision/data_spec.rb +54 -0
  36. data/spec/bard/provision/deploy_spec.rb +33 -0
  37. data/spec/bard/provision/http_spec.rb +57 -0
  38. data/spec/bard/provision/logrotation_spec.rb +34 -0
  39. data/spec/bard/provision/masterkey_spec.rb +63 -0
  40. data/spec/bard/provision/mysql_spec.rb +55 -0
  41. data/spec/bard/provision/passenger_spec.rb +81 -0
  42. data/spec/bard/provision/repo_spec.rb +208 -0
  43. data/spec/bard/provision/rvm_spec.rb +49 -0
  44. data/spec/bard/provision/ssh_spec.rb +229 -0
  45. data/spec/bard/provision/swapfile_spec.rb +32 -0
  46. data/spec/bard/provision/user_spec.rb +103 -0
  47. data/spec/bard/provision_spec.rb +28 -0
  48. data/spec/spec_helper.rb +6 -1
  49. metadata +96 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3203ba9146ef6ed6b43512e6e61d02c0f34769703a20bc7809316fedee18dbd2
4
- data.tar.gz: b8a8d8aa3ebd8163cb6679fecde47e6a9e961456ec3fc661f904775b36bb4283
3
+ metadata.gz: d7758630b558e557b178b1d598cec7ef6c5bd3d56769673372f4133ffc488451
4
+ data.tar.gz: 86df74f01dab9bdb30690340571d10b2bd51bbff3d70f1aeca4385d399542d76
5
5
  SHA512:
6
- metadata.gz: 36f776bfba32128c5584fd91bea060fb57423e648de3590c68a4be0dca835a553be992b3629704640d1530f94d6b25420302efe030de13f0fb8da1c1ea3394a5
7
- data.tar.gz: 591b17f6e3ac45fef9b579056777a6d33756cfb1ced970e75251856d782daddbe3e24786b744931b69275828b64e9ecb95d3f940a3edfe65d769f8a230581b2a
6
+ metadata.gz: bd955ce3572ec9c071f775ea15c78ce004ef0db2125105e0d72678057b65475d534cc21e408ca0900ef57735c13f30a7d1755714bc4dd0019469f73f66ed04c2
7
+ data.tar.gz: e6b64d2cef83b325161b5f7b5f990b400a245f7b5103f7539d3a500150f7c2ab863557350954213fec8f382aa7908142f19fb98df0304a921f486be77470187d
@@ -0,0 +1,22 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ ruby: [ "3.2", "3.3", "3.4" ]
9
+
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Checkout code
13
+ uses: actions/checkout@v4
14
+
15
+ - name: Set up Ruby
16
+ uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ bundler-cache: true
20
+
21
+ - name: Run tests
22
+ run: bundle exec rake
data/Gemfile CHANGED
@@ -3,3 +3,7 @@ source "http://rubygems.org"
3
3
  # Specify your gem's dependencies in bard.gemspec
4
4
  gemspec
5
5
 
6
+ group :test do
7
+ gem "simplecov", require: false
8
+ gem "webmock", require: false
9
+ end
data/bard.gemspec CHANGED
@@ -21,8 +21,9 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency "rvm"
22
22
  spec.add_dependency "term-ansicolor", ">= 1.0.3"
23
23
  spec.add_dependency "rbnacl"
24
+ spec.add_dependency "base64"
24
25
 
25
- spec.add_development_dependency "byebug"
26
+ spec.add_development_dependency "rake"
26
27
  spec.add_development_dependency "rspec"
27
- spec.add_development_dependency "cucumber"
28
+ spec.add_development_dependency "debug"
28
29
  end
data/lib/bard/git.rb CHANGED
@@ -21,9 +21,13 @@ module Bard
21
21
 
22
22
  def sha_of ref
23
23
  sha = `git rev-parse #{ref} 2>/dev/null`.chomp
24
- return sha if $?.success?
24
+ return sha if command_succeeded?
25
25
  nil # Branch doesn't exist
26
26
  end
27
+
28
+ def command_succeeded?
29
+ $?.success?
30
+ end
27
31
  end
28
32
  end
29
33
 
@@ -20,6 +20,11 @@ class Bard::Provision::Repo < Bard::Provision
20
20
  end
21
21
  print " Cloning repo,"
22
22
  provision_server.run! "git clone git@github.com:botandrosedesign/#{project_name}", home: true
23
+ else
24
+ if !on_latest_master?
25
+ print " Updating to latest master,"
26
+ update_to_latest_master!
27
+ end
23
28
  end
24
29
 
25
30
  puts " ✓"
@@ -47,5 +52,21 @@ class Bard::Provision::Repo < Bard::Provision
47
52
  def project_name
48
53
  server.project_name
49
54
  end
55
+
56
+ def on_latest_master?
57
+ provision_server.run [
58
+ "cd ~/#{project_name}",
59
+ "git fetch origin",
60
+ "[ $(git rev-parse HEAD) = $(git rev-parse origin/master) ]"
61
+ ].join(" && "), home: true, quiet: true
62
+ end
63
+
64
+ def update_to_latest_master!
65
+ provision_server.run! [
66
+ "cd ~/#{project_name}",
67
+ "git checkout master",
68
+ "git reset --hard origin/master"
69
+ ].join(" && "), home: true
70
+ end
50
71
  end
51
72
 
@@ -5,6 +5,11 @@ class Bard::Provision::SSH < Bard::Provision
5
5
  def call
6
6
  print "SSH:"
7
7
 
8
+ if password_auth_enabled?
9
+ print " Disabling password authentication,"
10
+ disable_password_auth!
11
+ end
12
+
8
13
  if !ssh_available?(provision_server.ssh_uri, port: target_port)
9
14
  if !ssh_available?(provision_server.ssh_uri)
10
15
  raise "can't find SSH on port #{target_port} or #{provision_server.ssh_uri.port || 22}"
@@ -48,4 +53,20 @@ class Bard::Provision::SSH < Bard::Provision
48
53
  port ||= ssh_uri.port || 22
49
54
  system "ssh-keyscan -p#{port} -H #{ssh_uri.host} >> ~/.ssh/known_hosts 2>/dev/null"
50
55
  end
56
+
57
+ def password_auth_enabled?
58
+ result = provision_server.run!(
59
+ %q{grep -E '^\s*PasswordAuthentication\s+yes' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf 2>/dev/null || true},
60
+ home: true,
61
+ capture: true
62
+ )
63
+ !!(result && !result.strip.empty?)
64
+ end
65
+
66
+ def disable_password_auth!
67
+ provision_server.run!(
68
+ %q{echo "PasswordAuthentication no" | sudo tee /etc/ssh/sshd_config.d/disable_password_auth.conf; sudo service ssh restart},
69
+ home: true
70
+ )
71
+ end
51
72
  end
data/lib/bard/server.rb CHANGED
@@ -73,8 +73,8 @@ module Bard
73
73
 
74
74
  def rsync_uri file_path=nil
75
75
  str = ssh_uri.dup.tap do |uri|
76
- uri.scheme = nil
77
- uri.port = nil
76
+ uri.send :set_scheme, nil
77
+ uri.send :set_port, nil
78
78
  end.to_s[2..]
79
79
  str += ":#{path}"
80
80
  str += "/#{file_path}" if file_path
data/lib/bard/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Bard
2
- VERSION = "1.4.8"
2
+ VERSION = "1.4.9"
3
3
  end
4
4
 
@@ -0,0 +1,139 @@
1
+ require "spec_helper"
2
+ require "bard/cli"
3
+ require "bard/cli/ci"
4
+ require "thor"
5
+
6
+ class TestCICLI < Thor
7
+ include Bard::CLI::CI
8
+
9
+ attr_reader :options
10
+
11
+ def initialize
12
+ super
13
+ @options = {}
14
+ end
15
+
16
+ def project_name
17
+ "test_project"
18
+ end
19
+ end
20
+
21
+ describe Bard::CLI::CI do
22
+ let(:cli) { TestCICLI.new }
23
+ let(:ci_runner) { double("ci_runner") }
24
+
25
+ before do
26
+ allow(cli).to receive(:puts)
27
+ allow(cli).to receive(:print)
28
+ allow(cli).to receive(:exit)
29
+ allow(cli).to receive(:red).and_return("")
30
+ allow($stdout).to receive(:flush)
31
+ allow(Bard::Git).to receive(:current_branch).and_return("feature-branch")
32
+ allow(Bard::CI).to receive(:new).and_return(ci_runner)
33
+ end
34
+
35
+ describe "#ci" do
36
+ it "should have a ci command" do
37
+ expect(cli).to respond_to(:ci)
38
+ end
39
+
40
+ context "when CI exists and status option is set" do
41
+ it "prints CI status and returns early" do
42
+ allow(cli).to receive(:options).and_return({ "status" => true })
43
+ allow(ci_runner).to receive(:exists?).and_return(true)
44
+ allow(ci_runner).to receive(:status).and_return("Build #123: SUCCESS")
45
+
46
+ expect(cli).to receive(:puts).with("Build #123: SUCCESS")
47
+ expect(ci_runner).not_to receive(:run)
48
+
49
+ cli.ci
50
+ end
51
+ end
52
+
53
+ context "when CI exists and running normally" do
54
+ before do
55
+ allow(cli).to receive(:options).and_return({})
56
+ allow(ci_runner).to receive(:exists?).and_return(true)
57
+ end
58
+
59
+ it "starts CI build and handles success" do
60
+ allow(ci_runner).to receive(:run).and_yield(30, 60).and_return(true)
61
+
62
+ expect(cli).to receive(:puts).with("Continuous integration: starting build on feature-branch...")
63
+ expect(cli).to receive(:puts).with("Continuous integration: success!")
64
+ expect(cli).to receive(:puts).with("Deploying...")
65
+
66
+ cli.ci
67
+ end
68
+
69
+ it "handles CI failure" do
70
+ allow(ci_runner).to receive(:run).and_yield(30, 60).and_return(false)
71
+ allow(ci_runner).to receive(:console).and_return("Test failed: spec/model_spec.rb")
72
+
73
+ expect(cli).to receive(:puts).with("Continuous integration: starting build on feature-branch...")
74
+ expect(cli).to receive(:puts).with("Test failed: spec/model_spec.rb")
75
+ expect(cli).to receive(:puts) # The puts with newline
76
+ expect(cli).to receive(:puts) # The "Automated tests failed!" message
77
+ expect(cli).to receive(:exit).with(1)
78
+
79
+ cli.ci
80
+ end
81
+
82
+ it "displays progress with estimated completion time" do
83
+ allow(ci_runner).to receive(:run).and_yield(30, 60).and_return(true)
84
+
85
+ expect(cli).to receive(:print).with("\x08" * " Estimated completion: 50%".length)
86
+ expect(cli).to receive(:print).with(" Estimated completion: 50%")
87
+
88
+ cli.ci
89
+ end
90
+
91
+ it "displays progress without estimated completion time" do
92
+ allow(ci_runner).to receive(:run).and_yield(45, nil).and_return(true)
93
+
94
+ expect(cli).to receive(:print).with("\x08" * " No estimated completion time. Elapsed time: 45 sec".length)
95
+ expect(cli).to receive(:print).with(" No estimated completion time. Elapsed time: 45 sec")
96
+
97
+ cli.ci
98
+ end
99
+ end
100
+
101
+ context "when CI doesn't exist" do
102
+ it "shows error message and exits" do
103
+ allow(cli).to receive(:options).and_return({})
104
+ allow(ci_runner).to receive(:exists?).and_return(false)
105
+
106
+ expect(cli).to receive(:puts) # "No CI found for test_project!"
107
+ expect(cli).to receive(:puts) # "Re-run with --skip-ci to bypass CI..."
108
+ expect(cli).to receive(:exit).with(1)
109
+
110
+ cli.ci
111
+ end
112
+ end
113
+
114
+ context "with custom branch" do
115
+ it "uses the specified branch" do
116
+ allow(cli).to receive(:options).and_return({})
117
+ allow(ci_runner).to receive(:exists?).and_return(true)
118
+ allow(ci_runner).to receive(:run).and_return(true)
119
+
120
+ expect(Bard::CI).to receive(:new).with("test_project", "develop", local: nil)
121
+ expect(cli).to receive(:puts).with("Continuous integration: starting build on develop...")
122
+
123
+ cli.ci("develop")
124
+ end
125
+ end
126
+
127
+ context "with local-ci option" do
128
+ it "passes local option to CI runner" do
129
+ allow(cli).to receive(:options).and_return({ "local-ci" => true })
130
+ allow(ci_runner).to receive(:exists?).and_return(true)
131
+ allow(ci_runner).to receive(:run).and_return(true)
132
+
133
+ expect(Bard::CI).to receive(:new).with("test_project", "feature-branch", local: true)
134
+
135
+ cli.ci
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+ require "bard/cli"
3
+ require "bard/cli/command"
4
+
5
+ class TestCommand < Bard::CLI::Command
6
+ desc "test_command", "test command description"
7
+ option :verbose, type: :boolean
8
+
9
+ def test_command
10
+ "executed"
11
+ end
12
+ end
13
+
14
+ describe Bard::CLI::Command do
15
+ let(:cli_mock) { double("cli") }
16
+ let(:command) { TestCommand.new(cli_mock) }
17
+
18
+ describe ".desc" do
19
+ it "sets command and description" do
20
+ expect(TestCommand.instance_variable_get(:@command)).to eq("test_command")
21
+ expect(TestCommand.instance_variable_get(:@description)).to eq("test command description")
22
+ end
23
+ end
24
+
25
+ describe ".option" do
26
+ it "sets option arguments" do
27
+ expect(TestCommand.instance_variable_get(:@option_args)).to eq([:verbose])
28
+ expect(TestCommand.instance_variable_get(:@option_kwargs)).to eq({type: :boolean})
29
+ end
30
+ end
31
+
32
+ describe ".setup" do
33
+ let(:cli_double) { double("cli") }
34
+
35
+ it "sets up the command on the CLI class" do
36
+ expect(cli_double).to receive(:desc).with("test_command", "test command description")
37
+ expect(cli_double).to receive(:option).with(:verbose, type: :boolean)
38
+ expect(cli_double).to receive(:define_method)
39
+
40
+ TestCommand.setup(cli_double)
41
+ end
42
+ end
43
+
44
+ describe "delegation" do
45
+ it "should delegate to the wrapped object" do
46
+ allow(cli_mock).to receive(:some_method).and_return("delegated")
47
+ expect(command.some_method).to eq("delegated")
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,77 @@
1
+ require "spec_helper"
2
+ require "bard/cli"
3
+ require "bard/cli/data"
4
+
5
+ require "thor"
6
+
7
+ require "term/ansicolor"
8
+
9
+ class TestCLI < Thor
10
+ include Bard::CLI::Data
11
+ include Term::ANSIColor
12
+
13
+ attr_reader :config
14
+
15
+ def initialize
16
+ @config = {}
17
+ end
18
+
19
+ def options
20
+ {}
21
+ end
22
+ end
23
+
24
+ describe Bard::CLI::Data do
25
+ let(:cli) { TestCLI.new }
26
+
27
+ it "should have a data command" do
28
+ expect(cli).to respond_to(:data)
29
+ end
30
+
31
+ context "data" do
32
+ let(:from) { double("from", key: :production, run!: nil, copy_file: nil, copy_dir: nil) }
33
+ let(:to) { double("to", key: :local, run!: nil) }
34
+
35
+ let(:config) do
36
+ double("config", data: [], :[] => nil).tap do |config|
37
+ allow(config).to receive(:[]).with("production").and_return(from)
38
+ allow(config).to receive(:[]).with("local").and_return(to)
39
+ end
40
+ end
41
+
42
+ before do
43
+ allow(cli).to receive(:config).and_return(config)
44
+ allow(cli).to receive(:options).and_return({from: "production", to: "local"})
45
+ end
46
+
47
+ it "should run the data command" do
48
+ expect(from).to receive(:run!).with("bin/rake db:dump")
49
+ expect(from).to receive(:copy_file).with("db/data.sql.gz", to: to, verbose: true)
50
+ expect(to).to receive(:run!).with("bin/rake db:load")
51
+ cli.data
52
+ end
53
+
54
+ context "pushing to production" do
55
+ let(:to) { double("to", key: :production, ping: ["https://example.com"]) }
56
+
57
+ before do
58
+ allow(cli).to receive(:options).and_return({from: "local", to: "production"})
59
+ allow(config).to receive(:[]).with("production").and_return(to)
60
+ allow(config).to receive(:[]).with("local").and_return(from)
61
+ end
62
+
63
+ it "should prevent pushing to production if the user does not confirm" do
64
+ expect(cli).to receive(:ask).and_return("no")
65
+ expect { cli.data }.to raise_error(SystemExit)
66
+ end
67
+
68
+ it "should allow pushing to production if the user confirms" do
69
+ expect(cli).to receive(:ask).and_return("https://example.com")
70
+ expect(from).to receive(:run!).with("bin/rake db:dump")
71
+ expect(from).to receive(:copy_file).with("db/data.sql.gz", to: to, verbose: true)
72
+ expect(to).to receive(:run!).with("bin/rake db:load")
73
+ cli.data
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,202 @@
1
+ require "spec_helper"
2
+ require "bard/cli"
3
+ require "bard/cli/deploy"
4
+ require "thor"
5
+
6
+ class TestDeployCLI < Thor
7
+ include Bard::CLI::Deploy
8
+
9
+ attr_reader :config, :options
10
+
11
+ def initialize
12
+ super
13
+ @config = {}
14
+ @options = {}
15
+ end
16
+
17
+ def project_name
18
+ "test_project"
19
+ end
20
+ end
21
+
22
+ describe Bard::CLI::Deploy do
23
+ let(:production_server) { double("production", run!: true, github_pages: false, path: "/var/www/test_project") }
24
+ let(:config) { { production: production_server } }
25
+ let(:cli) { TestDeployCLI.new }
26
+
27
+ before do
28
+ allow(cli).to receive(:config).and_return(config)
29
+ allow(cli).to receive(:puts)
30
+ allow(cli).to receive(:exit)
31
+ allow(cli).to receive(:run!)
32
+ allow(cli).to receive(:invoke)
33
+ allow(cli).to receive(:ping)
34
+ allow(cli).to receive(:green).and_return("")
35
+ allow(cli).to receive(:red).and_return("")
36
+ allow(cli).to receive(:yellow).and_return("")
37
+ allow(Bard::Git).to receive(:current_branch).and_return("feature-branch")
38
+ allow(Bard::Git).to receive(:up_to_date_with_remote?).and_return(true)
39
+ allow(Bard::Git).to receive(:fast_forward_merge?).and_return(true)
40
+ allow(cli).to receive(:`).and_return("")
41
+ end
42
+
43
+ describe "#deploy" do
44
+ it "should have a deploy command" do
45
+ expect(cli).to respond_to(:deploy)
46
+ end
47
+
48
+ context "when on master branch" do
49
+ before do
50
+ allow(Bard::Git).to receive(:current_branch).and_return("master")
51
+ allow(cli).to receive(:options).and_return({})
52
+ end
53
+
54
+ context "when up to date with remote" do
55
+ it "skips push and runs CI then deploys" do
56
+ allow(Bard::Git).to receive(:up_to_date_with_remote?).and_return(true)
57
+
58
+ expect(cli).not_to receive(:run!).with(/git push/)
59
+ expect(cli).to receive(:invoke).with(:ci, ["master"], {})
60
+ expect(production_server).to receive(:run!).with("git pull origin master && bin/setup")
61
+ expect(cli).to receive(:puts) # "Deploy Succeeded"
62
+ expect(cli).to receive(:ping).with(:production)
63
+
64
+ cli.deploy
65
+ end
66
+ end
67
+
68
+ context "when not up to date with remote" do
69
+ it "pushes master then runs CI and deploys" do
70
+ allow(Bard::Git).to receive(:up_to_date_with_remote?).and_return(false)
71
+
72
+ expect(cli).to receive(:run!).with("git push origin master:master")
73
+ expect(cli).to receive(:invoke).with(:ci, ["master"], {})
74
+ expect(production_server).to receive(:run!).with("git pull origin master && bin/setup")
75
+
76
+ cli.deploy
77
+ end
78
+ end
79
+
80
+ context "with skip-ci option" do
81
+ it "skips CI step" do
82
+ allow(cli).to receive(:options).and_return({ "skip-ci" => true })
83
+
84
+ expect(cli).not_to receive(:invoke).with(:ci, anything, anything)
85
+ expect(production_server).to receive(:run!).with("git pull origin master && bin/setup")
86
+
87
+ cli.deploy
88
+ end
89
+ end
90
+ end
91
+
92
+ context "when on feature branch" do
93
+ before do
94
+ allow(cli).to receive(:options).and_return({})
95
+ end
96
+
97
+ context "with fast-forward merge possible" do
98
+ it "fetches master, pushes branch, runs CI, merges to master, and deploys" do
99
+ expect(cli).to receive(:run!).with("git fetch origin master:master")
100
+ expect(cli).to receive(:run!).with("git push -f origin feature-branch:feature-branch")
101
+ expect(cli).to receive(:invoke).with(:ci, ["feature-branch"], {})
102
+ expect(cli).to receive(:run!).with("git push origin feature-branch:master")
103
+ expect(cli).to receive(:run!).with("git fetch origin master:master")
104
+ expect(production_server).to receive(:run!).with("git pull origin master && bin/setup")
105
+
106
+ cli.deploy
107
+ end
108
+
109
+ it "deletes the feature branch after successful deploy" do
110
+ expect(cli).to receive(:puts).with("Deleting branch: feature-branch")
111
+ expect(cli).to receive(:run!).with("git push --delete origin feature-branch")
112
+ expect(cli).to receive(:run!).with("git checkout master")
113
+ expect(cli).to receive(:run!).with("git branch -D feature-branch")
114
+
115
+ cli.deploy
116
+ end
117
+ end
118
+
119
+ context "when rebase is needed" do
120
+ it "attempts rebase before proceeding" do
121
+ allow(Bard::Git).to receive(:fast_forward_merge?).and_return(false)
122
+
123
+ expect(cli).to receive(:puts).with("The master branch has advanced. Attempting rebase...")
124
+ expect(cli).to receive(:run!).with("git rebase origin/master")
125
+ expect(cli).to receive(:run!).with("git push -f origin feature-branch:feature-branch")
126
+
127
+ cli.deploy
128
+ end
129
+ end
130
+ end
131
+
132
+ context "with github remote" do
133
+ it "pushes to github remote" do
134
+ allow(cli).to receive(:`).with("git remote").and_return("origin\ngithub\n")
135
+
136
+ expect(cli).to receive(:run!).with("git push github")
137
+
138
+ cli.deploy
139
+ end
140
+ end
141
+
142
+ context "with clone option" do
143
+ it "clones repository and sets up application" do
144
+ allow(cli).to receive(:options).and_return({ clone: true })
145
+
146
+ expect(production_server).to receive(:run!).with("git clone git@github.com:botandrosedesign/test_project /var/www/test_project", home: true)
147
+ expect(cli).to receive(:invoke).with(:master_key, [], from: "local", to: :production)
148
+ expect(production_server).to receive(:run!).with("bin/setup && bard setup")
149
+
150
+ cli.deploy
151
+ end
152
+ end
153
+
154
+ context "with github pages" do
155
+ it "deploys to github pages" do
156
+ allow(production_server).to receive(:github_pages).and_return(true)
157
+ github_pages = double("github_pages")
158
+ allow(Bard::GithubPages).to receive(:new).and_return(github_pages)
159
+
160
+ expect(github_pages).to receive(:deploy).with(production_server)
161
+
162
+ cli.deploy
163
+ end
164
+ end
165
+
166
+ context "with custom deployment target" do
167
+ let(:staging_server) { double("staging", run!: true, github_pages: false) }
168
+
169
+ before do
170
+ allow(config).to receive(:[]).with(:staging).and_return(staging_server)
171
+ end
172
+
173
+ it "deploys to specified target" do
174
+ expect(staging_server).to receive(:run!).with("git pull origin master && bin/setup")
175
+ expect(cli).to receive(:ping).with(:staging)
176
+
177
+ cli.deploy(:staging)
178
+ end
179
+ end
180
+
181
+ context "when command fails" do
182
+ it "handles errors gracefully" do
183
+ allow(cli).to receive(:run!).and_raise(Bard::Command::Error.new("Git push failed"))
184
+
185
+ expect(cli).to receive(:puts).with(/Running command failed/)
186
+ expect(cli).to receive(:exit).with(1)
187
+
188
+ cli.deploy
189
+ end
190
+ end
191
+
192
+ context "with local-ci option" do
193
+ it "passes local-ci option to CI invocation" do
194
+ allow(cli).to receive(:options).and_return({ "local-ci" => true })
195
+
196
+ expect(cli).to receive(:invoke).with(:ci, ["feature-branch"], { "local-ci" => true })
197
+
198
+ cli.deploy
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+ require "bard/cli"
3
+ require "bard/cli/hurt"
4
+ require "thor"
5
+
6
+ class TestHurtCLI < Thor
7
+ include Bard::CLI::Hurt
8
+ end
9
+
10
+ describe Bard::CLI::Hurt do
11
+ let(:cli) { TestHurtCLI.new }
12
+
13
+ before do
14
+ allow(cli).to receive(:puts)
15
+ allow(cli).to receive(:system)
16
+ end
17
+
18
+ describe "#hurt" do
19
+ it "should have a hurt command" do
20
+ expect(cli).to respond_to(:hurt)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ require "spec_helper"
2
+ require "bard/cli"
3
+ require "bard/cli/install"
4
+ require "thor"
5
+
6
+ class TestInstallCLI < Thor
7
+ include Bard::CLI::Install
8
+ end
9
+
10
+ describe Bard::CLI::Install do
11
+ let(:cli) { TestInstallCLI.new }
12
+
13
+ describe "#install" do
14
+ it "should have an install command" do
15
+ expect(cli).to respond_to(:install)
16
+ end
17
+
18
+ it "should copy install files to bin directory" do
19
+ expect(cli).to receive(:system).with(/cp -R .*install_files\/\* bin\//)
20
+ expect(cli).to receive(:system).with(/cp -R .*install_files\/\.github \.\//)
21
+
22
+ cli.install
23
+ end
24
+ end
25
+ end