bard 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf24b5fc8c53912b9568bbba566570d1883ab489e7d72dd01869dd1efcdcea8e
4
- data.tar.gz: 2b069ea15ef1dbc21beccd0c909eedae482909c1a4a3a22fe7eb2e88d765118d
3
+ metadata.gz: 5827b2cb31e34dfbdf111fbdfd52226e5440c3c747de441b8c70bb24a4de9309
4
+ data.tar.gz: 4a8664eebd8bed4cfdd1fdb85938dbe1356817e5ea21f159c7919bb3be0a8f63
5
5
  SHA512:
6
- metadata.gz: 3ff5b076f3ef83cd1d1fbf9eec8a510b4b1c6fcde36b0a9f823e48b694d29d0804869c0520dcd04095ff9ec974463c214f11f9e342eef478c30adc5827b0a21a
7
- data.tar.gz: 64477c11e3e15b746c892329c67fbcb7c33872e2cc58c13aa8aee1ce88ee09976a539704e7f2e7260936b7d38956731542fac34c8684ebdb55943dafa2a3764b
6
+ metadata.gz: 04fc3e6e447298b74e73262a30bb188845ec86f39a63779437fb1a43d6b7a9bc2fbb9ced0e6c99df681704370f42526674d9c0d4cc83f12e0da09782c7bddd0a
7
+ data.tar.gz: 54c654366cb8400bd5a7549b586662617e236bd96f9c48f299776d0454eafeb6b9a75a468582999fc4d88cfddb3856637cb9f3e6973adec2342944bda5d63eb0
data/Rakefile CHANGED
@@ -1,2 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
2
7
 
@@ -17,7 +17,7 @@ Feature: Bard can check its environment for missing dependencies and potential p
17
17
  And the database is missing
18
18
  When I type "bard check"
19
19
  Then I should see the fatal error "missing database"
20
-
20
+
21
21
  Scenario: Bard check detects pending migrations
22
22
  Given a shared rails project
23
23
  And a commit with a new migration
@@ -42,7 +42,7 @@ Feature: bard push
42
42
  Then on staging, there should be one new submodule
43
43
  And the submodule branch should match the submodule origin branch
44
44
  And on staging, the submodule working directory should be clean
45
-
45
+
46
46
  Scenario: Pushing a change that includes a submodule update
47
47
  Given a submodule
48
48
  And a commit with a submodule update
@@ -3,11 +3,11 @@ Given /^a shared rails project$/ do
3
3
  Dir.foreach "#{ROOT}/tmp" do |file|
4
4
  FileUtils.rm_rf("#{ROOT}/tmp/#{file}") unless %w(fixtures . ..).include? file
5
5
  end
6
-
6
+
7
7
  # SETUP
8
8
  Dir.chdir ROOT
9
9
  `cp -r tmp/fixtures/* tmp/`
10
-
10
+
11
11
  Dir.chdir 'tmp'
12
12
  @repos = {}
13
13
  %w(development_a development_b staging production).each do |env|
@@ -44,11 +44,11 @@ Then /^I should see the fatal error "([^\"]*)"$/ do |error_message|
44
44
  end
45
45
 
46
46
  Then /^I should see the warning "([^\"]*)"$/ do |warning_message|
47
- @stderr.should include(warning_message)
47
+ @stderr.should include(warning_message)
48
48
  end
49
49
 
50
50
  Then /^I should see "([^\"]*)"$/ do |message|
51
- @stdout.should include(message)
51
+ @stdout.should include(message)
52
52
  end
53
53
 
54
54
  Then /^debug$/ do
@@ -18,7 +18,7 @@ if File.exist?("/dev/shm")
18
18
  FileUtils.rm_rf tmp_dir
19
19
  FileUtils.mkdir tmp_dir
20
20
  `ln -s #{tmp_dir} tmp`
21
- else
21
+ else
22
22
  FileUtils.rm_rf "tmp"
23
23
  FileUtils.mkdir "tmp"
24
24
  end
@@ -18,7 +18,7 @@ end
18
18
 
19
19
  def file_inject(file_name, sentinel, string, before_after=:after)
20
20
  gsub_file file_name, /(#{Regexp.escape(sentinel)})/mi do |match|
21
- if before_after == :after
21
+ if before_after == :after
22
22
  "#{match}\n#{string}"
23
23
  else
24
24
  "#{string}\n#{match}"
@@ -27,17 +27,14 @@ module Bard
27
27
  @run.console
28
28
  end
29
29
 
30
- def last_response
31
- end
32
-
33
30
  def status
34
31
  last_run = api.last_run
35
32
  if last_run.building?
36
- puts "Building..."
33
+ "Building..."
37
34
  elsif last_run.success?
38
- puts "Succeeded!"
35
+ "Succeeded!"
39
36
  elsif last_run.failure?
40
- puts "Failed!\n\n#{last_run.console}"
37
+ "Failed!\n\n#{last_run.console}"
41
38
  else
42
39
  raise "Unknown job status: #{last_run.inspect}"
43
40
  end
data/lib/bard/ci/local.rb CHANGED
@@ -28,8 +28,6 @@ module Bard
28
28
  @console
29
29
  end
30
30
 
31
- attr_accessor :last_response
32
-
33
31
  private
34
32
 
35
33
  def start
data/lib/bard/ci.rb CHANGED
@@ -8,18 +8,10 @@ module Bard
8
8
  @local = !!local
9
9
  end
10
10
 
11
- attr_reader :project_name, :branch, :runner
12
-
13
- def sha
14
- @sha ||= `git rev-parse #{branch}`.chomp
15
- end
16
-
17
- def runner
18
- @runner ||= choose_runner_class.new(project_name, branch, sha)
19
- end
20
-
21
11
  extend Forwardable
22
- delegate [:run, :exists?, :console, :last_response, :status] => :runner
12
+ delegate [:run, :exists?, :console, :status] => :runner
13
+
14
+ private
23
15
 
24
16
  def local?
25
17
  @local
@@ -33,7 +25,13 @@ module Bard
33
25
  !local? && !github_actions?
34
26
  end
35
27
 
36
- private
28
+ def runner
29
+ @runner ||= choose_runner_class.new(@project_name, @branch, sha)
30
+ end
31
+
32
+ def sha
33
+ @sha ||= `git rev-parse #{@branch}`.chomp
34
+ end
37
35
 
38
36
  def choose_runner_class
39
37
  if local?
@@ -0,0 +1,51 @@
1
+ require "bard/ci"
2
+ require "bard/git"
3
+
4
+ module Bard::CLI::CI
5
+ def self.included mod
6
+ mod.class_eval do
7
+
8
+ option :"local-ci", type: :boolean
9
+ option :status, type: :boolean
10
+ desc "ci [branch=HEAD]", "runs ci against BRANCH"
11
+ def ci branch=Bard::Git.current_branch
12
+ ci = Bard::CI.new(project_name, branch, local: options["local-ci"])
13
+ if ci.exists?
14
+ return puts ci.status if options["status"]
15
+
16
+ puts "Continuous integration: starting build on #{branch}..."
17
+
18
+ success = ci.run do |elapsed_time, last_time|
19
+ if last_time
20
+ percentage = (elapsed_time.to_f / last_time.to_f * 100).to_i
21
+ output = " Estimated completion: #{percentage}%"
22
+ else
23
+ output = " No estimated completion time. Elapsed time: #{elapsed_time} sec"
24
+ end
25
+ print "\x08" * output.length
26
+ print output
27
+ $stdout.flush
28
+ end
29
+
30
+ if success
31
+ puts
32
+ puts "Continuous integration: success!"
33
+ puts "Deploying..."
34
+ else
35
+ puts
36
+ puts ci.console
37
+ puts red("Automated tests failed!")
38
+ exit 1
39
+ end
40
+
41
+ else
42
+ puts red("No CI found for #{project_name}!")
43
+ puts "Re-run with --skip-ci to bypass CI, if you absolutely must, and know what you're doing."
44
+ exit 1
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,45 @@
1
+ require "bard/command"
2
+
3
+ module Bard::CLI::Data
4
+ def self.included mod
5
+ mod.class_eval do
6
+
7
+ desc "data --from=production --to=local", "copy database and assets from from to to"
8
+ option :from, default: "production"
9
+ option :to, default: "local"
10
+ def data
11
+ from = config[options[:from]]
12
+ to = config[options[:to]]
13
+
14
+ if to.key == :production
15
+ url = to.ping.first
16
+ puts yellow "WARNING: You are about to push data to production, overwriting everything that is there!"
17
+ answer = ask("If you really want to do this, please type in the full HTTPS url of the production server:")
18
+ if answer != url
19
+ puts red("!!! ") + "Failed! We expected #{url}. Is this really where you want to overwrite all the data?"
20
+ exit 1
21
+ end
22
+ end
23
+
24
+ puts "Dumping #{from.key} database to file..."
25
+ from.run! "bin/rake db:dump"
26
+
27
+ puts "Transfering file from #{from.key} to #{to.key}..."
28
+ from.copy_file "db/data.sql.gz", to: to, verbose: true
29
+
30
+ puts "Loading file into #{to.key} database..."
31
+ to.run! "bin/rake db:load"
32
+
33
+ config.data.each do |path|
34
+ puts "Synchronizing files in #{path}..."
35
+ from.copy_dir path, to: to, verbose: true
36
+ end
37
+ rescue Bard::Command::Error => e
38
+ puts red("!!! ") + "Running command failed: #{yellow(e.message)}"
39
+ exit 1
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,64 @@
1
+ require "bard/git"
2
+ require "bard/command"
3
+
4
+ module Bard::CLI::Deploy
5
+ def self.included mod
6
+ mod.class_eval do
7
+
8
+ option :"skip-ci", type: :boolean
9
+ option :"local-ci", type: :boolean
10
+ desc "deploy [TO=production]", "checks that current branch is a ff with master, checks with ci, merges into master, deploys to target, and then deletes branch."
11
+ def deploy to=:production
12
+ branch = Bard::Git.current_branch
13
+
14
+ if branch == "master"
15
+ if !Bard::Git.up_to_date_with_remote?(branch)
16
+ run! "git push origin #{branch}:#{branch}"
17
+ end
18
+ invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
19
+
20
+ else
21
+ run! "git fetch origin master:master"
22
+
23
+ unless Bard::Git.fast_forward_merge?("origin/master", branch)
24
+ puts "The master branch has advanced. Attempting rebase..."
25
+ run! "git rebase origin/master"
26
+ end
27
+
28
+ run! "git push -f origin #{branch}:#{branch}"
29
+
30
+ invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
31
+
32
+ run! "git push origin #{branch}:master"
33
+ run! "git fetch origin master:master"
34
+ end
35
+
36
+ if `git remote` =~ /\bgithub\b/
37
+ run! "git push github"
38
+ end
39
+
40
+ config[to].run! "git pull origin master && bin/setup"
41
+
42
+ puts green("Deploy Succeeded")
43
+
44
+ if branch != "master"
45
+ puts "Deleting branch: #{branch}"
46
+ run! "git push --delete origin #{branch}"
47
+
48
+ if branch == Bard::Git.current_branch
49
+ run! "git checkout master"
50
+ end
51
+
52
+ run! "git branch -D #{branch}"
53
+ end
54
+
55
+ ping to
56
+ rescue Bard::Command::Error => e
57
+ puts red("!!! ") + "Running command failed: #{yellow(e.message)}"
58
+ exit 1
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,20 @@
1
+ module Bard::CLI::Hurt
2
+ def self.included mod
3
+ mod.class_eval do
4
+
5
+ desc "hurt <command>", "reruns a command until it fails"
6
+ def hurt *args
7
+ (1..).each do |count|
8
+ puts "Running attempt #{count}"
9
+ system *args
10
+ unless $?.success?
11
+ puts "Ran #{count-1} times before failing"
12
+ break
13
+ end
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,16 @@
1
+ module Bard::CLI::Install
2
+ def self.included mod
3
+ mod.class_eval do
4
+
5
+ desc "install", "copies bin/setup and bin/ci scripts into current project."
6
+ def install
7
+ install_files_path = File.expand_path(File.join(__dir__, "../../install_files/*"))
8
+ system "cp -R #{install_files_path} bin/"
9
+ github_files_path = File.expand_path(File.join(__dir__, "../../install_files/.github"))
10
+ system "cp -R #{github_files_path} ./"
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,17 @@
1
+ module Bard::CLI::MasterKey
2
+ def self.included mod
3
+ mod.class_eval do
4
+
5
+ desc "master_key --from=production --to=local", "copy master key from from to to"
6
+ option :from, default: "production"
7
+ option :to, default: "local"
8
+ def master_key
9
+ from = config[options[:from]]
10
+ to = config[options[:to]]
11
+ from.copy_file "config/master.key", to:
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,13 @@
1
+ module Bard::CLI::Open
2
+ def self.included mod
3
+ mod.class_eval do
4
+
5
+ desc "open [server=production]", "opens the url in the web browser."
6
+ def open server=:production
7
+ exec "xdg-open #{config[server].ping.first}"
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,18 @@
1
+ require "bard/ping"
2
+
3
+ module Bard::CLI::Ping
4
+ def self.included mod
5
+ mod.class_eval do
6
+
7
+ desc "ping [server=production]", "hits the server over http to verify that its up."
8
+ def ping server=:production
9
+ server = config[server]
10
+ down_urls = Bard::Ping.call(config[server])
11
+ down_urls.each { |url| puts "#{url} is down!" }
12
+ exit 1 if down_urls.any?
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,15 @@
1
+ require "bard/provision"
2
+
3
+ module Bard::CLI::Provision
4
+ def self.included mod
5
+ mod.class_eval do
6
+
7
+ desc "provision [ssh_url]", "takes an ssh url to a raw ubuntu 22.04 install, and readies it in the shape of :production"
8
+ def provision ssh_url
9
+ Bard::Provision.call(config, ssh_url.dup) # dup unfreezes the string for later mutation
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,24 @@
1
+ require "bard/command"
2
+
3
+ module Bard::CLI::Run
4
+ def self.included mod
5
+ mod.class_eval do
6
+
7
+ # HACK: we don't use Thor::Base#run, so its okay to stomp on it here
8
+ original_verbose, $VERBOSE = $VERBOSE, nil
9
+ Thor::THOR_RESERVED_WORDS -= ["run"]
10
+ $VERBOSE = original_verbose
11
+
12
+ desc "run <command>", "run the given command on production"
13
+ def run *args
14
+ server = config[:production]
15
+ server.run! *args, verbose: true
16
+ rescue Bard::Command::Error => e
17
+ puts red("!!! ") + "Running command failed: #{yellow(e.message)}"
18
+ exit 1
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,45 @@
1
+ require "uri"
2
+
3
+ module Bard::CLI::Setup
4
+ def self.included mod
5
+ mod.class_eval do
6
+
7
+ desc "setup", "installs app in nginx"
8
+ def setup
9
+ path = "/etc/nginx/sites-available/#{project_name}"
10
+ dest_path = path.sub("sites-available", "sites-enabled")
11
+ server_name = case ENV["RAILS_ENV"]
12
+ when "production"
13
+ (config[:production].ping.map do |str|
14
+ "*.#{URI.parse(str).host}"
15
+ end + ["_"]).join(" ")
16
+ when "staging" then "#{project_name}.botandrose.com"
17
+ else "#{project_name}.localhost"
18
+ end
19
+
20
+ system "sudo tee #{path} >/dev/null <<-EOF
21
+ server {
22
+ listen 80;
23
+ server_name #{server_name};
24
+
25
+ root #{Dir.pwd}/public;
26
+ passenger_enabled on;
27
+
28
+ location ~* \\.(ico|css|js|gif|jp?g|png|webp) {
29
+ access_log off;
30
+ if (\\$request_filename ~ \"-[0-9a-f]{32}\\.\") {
31
+ expires max;
32
+ add_header Cache-Control public;
33
+ }
34
+ }
35
+ gzip_static on;
36
+ }
37
+ EOF"
38
+ system "sudo ln -sf #{path} #{dest_path}" if !File.exist?(dest_path)
39
+ system "sudo service nginx restart"
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,14 @@
1
+ module Bard::CLI::SSH
2
+ def self.included mod
3
+ mod.class_eval do
4
+
5
+ option :home, type: :boolean
6
+ desc "ssh [to=production]", "logs into the specified server via SSH"
7
+ def ssh to=:production
8
+ config[to].exec! "exec $SHELL -l", home: options[:home]
9
+ end
10
+
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,27 @@
1
+ require "bard/git"
2
+ require "bard/command"
3
+
4
+ module Bard::CLI::Stage
5
+ def self.included mod
6
+ mod.class_eval do
7
+
8
+ desc "stage [branch=HEAD]", "pushes current branch, and stages it"
9
+ def stage branch=Bard::Git.current_branch
10
+ unless config.servers.key?(:production)
11
+ raise Thor::Error.new("`bard stage` is disabled until a production server is defined. Until then, please use `bard deploy` to deploy to the staging server.")
12
+ end
13
+
14
+ run! "git push -u origin #{branch}", verbose: true
15
+ config[:staging].run! "git fetch && git checkout -f origin/#{branch} && bin/setup"
16
+ puts green("Stage Succeeded")
17
+
18
+ ping :staging
19
+ rescue Bard::Command::Error => e
20
+ puts red("!!! ") + "Running command failed: #{yellow(e.message)}"
21
+ exit 1
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,13 @@
1
+ module Bard::CLI::Vim
2
+ def self.included mod
3
+ mod.class_eval do
4
+
5
+ desc "vim [branch=master]", "open all files that have changed since master"
6
+ def vim branch="master"
7
+ exec "vim -p `git diff #{branch} --name-only | grep -v sass$ | tac`"
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+