Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ source "https://rubygems.org"
# Specify your gem's dependencies in gotsha.gemspec
gemspec

gem "base64"
gem "irb"
gem "rake", "~> 13.0"
gem "rspec", "~> 3.0"
Expand Down
1 change: 1 addition & 0 deletions lib/gotsha.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
require_relative "gotsha/bash_command"
require_relative "gotsha/config"
require_relative "gotsha/errors"
require_relative "gotsha/remote_resolver"
require_relative "gotsha/user_config"
require_relative "gotsha/version"

Expand Down
8 changes: 4 additions & 4 deletions lib/gotsha/action_dispatcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Gotsha
class ActionDispatcher
SKIP_CONFIG_VERIFICATION_FOR = %w[init configure uninstall].freeze
SKIP_CONFIG_VERIFICATION_FOR = %w[init configure uninstall help -h --help].freeze
DEFAULT_ACTION = "help"
HELP_ACTION_SHORTCUT = "-h"
VERSION_ACTION_SHORTCUT = "-v"
Expand All @@ -16,13 +16,13 @@ def self.call(action_name = DEFAULT_ACTION, *args)
def call(action_name, *args)
@action_name = action_name

return Actions::Help.new.call(action_name) if args == [HELP_ACTION_SHORTCUT]
return Actions::Help.new.call(action_name) if args == ["--help"]

verify_configuration!

action_class.new.call(*args)
rescue ArgumentError
return Actions::Help.new.call(action_name) if args == [HELP_ACTION_SHORTCUT]
return Actions::Help.new.call(action_name) if args == ["--help"]

raise Errors::HardFail, "too many arguments"
end

Expand Down
4 changes: 3 additions & 1 deletion lib/gotsha/actions/commit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ class Commit
DESCRIPTION = "runs tests on a dummy commit for manual sign-off"

def call
BashCommand.silent_run!('git -c core.hooksPath=/dev/null commit --allow-empty -m "Run Gotsha"')
command = BashCommand.silent_run!('git -c core.hooksPath=/dev/null commit --allow-empty -m "Run Gotsha"')

raise Errors::HardFail, "something went wrong" unless command.success?

Test.new.call
end
Expand Down
11 changes: 9 additions & 2 deletions lib/gotsha/actions/fetch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@ class Fetch
DESCRIPTION = "fetches Gotsha test results from remote"

def call
command = BashCommand.silent_run!("git fetch --force origin 'refs/notes/gotsha:refs/notes/gotsha'")
remote = RemoteResolver.resolve
command = BashCommand.silent_run!("git fetch --force #{remote} 'refs/notes/gotsha:refs/notes/gotsha'")

raise(Errors::HardFail, "something went wrong") unless command.success?
raise(Errors::HardFail, "something went wrong") unless command.success? || missing_notes_ref?(command)

"fetched"
end

private

def missing_notes_ref?(command)
command.text_output.include?("couldn't find remote ref refs/notes/gotsha")
end
end
end
end
3 changes: 2 additions & 1 deletion lib/gotsha/actions/push.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class Push
DESCRIPTION = "pushes Gotsha test results to remote"

def call
command = BashCommand.silent_run!("git push --no-verify --force origin refs/notes/gotsha:refs/notes/gotsha")
remote = RemoteResolver.resolve
command = BashCommand.silent_run!("git push --no-verify --force #{remote} refs/notes/gotsha:refs/notes/gotsha")

raise(Errors::HardFail, "something went wrong") unless command.success?

Expand Down
1 change: 1 addition & 0 deletions lib/gotsha/actions/uninstall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def call

File.exist?(Config::CONFIG_DIR) && FileUtils.rm_rf(Config::CONFIG_DIR)
File.exist?(Config::GH_CONFIG_FILE) && FileUtils.rm(Config::GH_CONFIG_FILE)
File.exist?(Config::GL_CONFIG_FILE) && FileUtils.rm(Config::GL_CONFIG_FILE)

puts "Unsetting Git hooks path..."
BashCommand.silent_run!("git config --unset core.hooksPath")
Expand Down
27 changes: 27 additions & 0 deletions lib/gotsha/remote_resolver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

require "open3"

module Gotsha
module RemoteResolver
def self.resolve
branch_name = BashCommand.run!("git branch --show-current").text_output
return "origin" if branch_name.empty?

push_remote = git_config("branch.#{branch_name}.pushRemote")
return push_remote unless push_remote.empty?

branch_remote = git_config("branch.#{branch_name}.remote")
return branch_remote unless branch_remote.empty?

"origin"
end

def self.git_config(key)
stdout, _status = Open3.capture2("git", "config", "--get", key)
stdout.strip
end

private_class_method :git_config
end
end
15 changes: 13 additions & 2 deletions lib/gotsha/user_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,26 @@ module Gotsha
class UserConfig
def self.get(key)
config = new.to_h
env_key = "GOTSHA_#{key.to_s.upcase}"
env_value = ENV[env_key]

ENV["GOTSHA_#{key.to_s.upcase}"] || # this allows changing config via ENV vars
config[key]
return config[key] if key.to_sym == :commands
return coerce_env_value(env_value) unless env_value.nil?

config[key]
end

def self.blank?
new.to_h.empty?
end

def self.coerce_env_value(value)
return true if value == "true"
return false if value == "false"

value
end

def to_h
TomlRB.load_file(Config::CONFIG_FILE).transform_keys(&:to_sym)
rescue Errno::ENOENT
Expand Down
55 changes: 52 additions & 3 deletions spec/gotsha/action_dispatcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
allow(Gotsha::UserConfig).to receive(:get).with(:ci).and_return(false)
allow(Gotsha::UserConfig).to receive(:get).with(:autogenerated).and_return(false)
allow(Gotsha::BashCommand).to receive(:run!).with("git config core.hooksPath")
.and_return(instance_double(Gotsha::BashCommand, text_output: Gotsha::Config::HOOKS_DIR))
.and_return(
instance_double(
Gotsha::BashCommand,
text_output: Gotsha::Config::HOOKS_DIR
)
)
end

describe "without any action name" do
Expand Down Expand Up @@ -38,6 +43,8 @@
end

describe "with autogenerated config" do
let(:action) { "status" }

before do
allow(Gotsha::UserConfig).to receive(:get).and_return(true)

Expand All @@ -46,7 +53,7 @@

it "fails with proper error" do
expect do
described_class.call
described_class.call(action)
end.to raise_exception(
Gotsha::Errors::HardFail,
"autogenerated config detected! Please, remove `autogenerated = true` from `.gotsha/config.toml` " \
Expand All @@ -63,9 +70,11 @@
end

context "with other action than `init`" do
let(:action) { "status" }

it "fails with proper error" do
expect do
described_class.call
described_class.call(action)
end.to raise_exception(
Gotsha::Errors::HardFail,
"config files not found, please run `gotsha init` first"
Expand All @@ -80,6 +89,46 @@
described_class.call(:init)
end
end

context "with `help` action" do
it "calls the help action without exception" do
expect_any_instance_of(Gotsha::Actions::Help).to receive(:call)

described_class.call(:help)
end
end

context "with `-h` shortcut" do
it "calls the help action without exception" do
expect_any_instance_of(Gotsha::Actions::Help).to receive(:call)

described_class.call("-h")
end
end

context "with `--help` shortcut" do
it "calls the help action without exception" do
expect_any_instance_of(Gotsha::Actions::Help).to receive(:call)

described_class.call("--help")
end
end

context "with a valid command followed by `-h`" do
it "calls the help action without exception" do
expect_any_instance_of(Gotsha::Actions::Help).to receive(:call).with("commit")

described_class.call("commit", "-h")
end
end

context "with a valid command followed by `--help`" do
it "calls the help action without exception" do
expect_any_instance_of(Gotsha::Actions::Help).to receive(:call).with("commit")

described_class.call("commit", "--help")
end
end
end

describe "with help action shortcut" do
Expand Down
18 changes: 18 additions & 0 deletions spec/gotsha/actions/commit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,31 @@
RSpec.describe Gotsha::Actions::Commit do
describe "commit" do
it "creates a blank commit and triggers `test` on it" do
git_commit = double("git_commit", success?: true)

expect(Gotsha::BashCommand)
.to receive(:silent_run!)
.with("git -c core.hooksPath=/dev/null commit --allow-empty -m \"Run Gotsha\"")
.and_return(git_commit)

expect_any_instance_of(Gotsha::Actions::Test).to(receive(:call))

described_class.new.call
end

it "does not trigger `test` when the blank commit fails" do
git_commit = double("git_commit", success?: false)

expect(Gotsha::BashCommand)
.to receive(:silent_run!)
.with("git -c core.hooksPath=/dev/null commit --allow-empty -m \"Run Gotsha\"")
.and_return(git_commit)

expect_any_instance_of(Gotsha::Actions::Test).not_to receive(:call)

expect do
described_class.new.call
end.to raise_error(Gotsha::Errors::HardFail, "something went wrong")
end
end
end
46 changes: 40 additions & 6 deletions spec/gotsha/actions/fetch_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,47 @@
describe "fetch" do
let(:git_command_mock) { double("git", success?: true) }

it "calls the Git command to fetch notes" do
expect(Gotsha::BashCommand)
.to receive(:silent_run!)
.with("git fetch --force origin 'refs/notes/gotsha:refs/notes/gotsha'")
.and_return(git_command_mock)
before do
allow(Gotsha::RemoteResolver).to receive(:resolve).and_return(remote_name)
end

context "with default remote" do
let(:remote_name) { "origin" }

it "calls the Git command to fetch notes" do
expect(Gotsha::BashCommand)
.to receive(:silent_run!)
.with("git fetch --force origin 'refs/notes/gotsha:refs/notes/gotsha'")
.and_return(git_command_mock)

expect(described_class.new.call).to eq("fetched")
end
end

context "with custom remote" do
let(:remote_name) { "upstream" }

it "uses the resolved remote" do
expect(Gotsha::BashCommand)
.to receive(:silent_run!)
.with("git fetch --force upstream 'refs/notes/gotsha:refs/notes/gotsha'")
.and_return(git_command_mock)

expect(described_class.new.call).to eq("fetched")
end
end

context "when remote notes ref does not exist" do
let(:remote_name) { "origin" }

it "does not fail" do
expect(Gotsha::BashCommand)
.to receive(:silent_run!)
.with("git fetch --force origin 'refs/notes/gotsha:refs/notes/gotsha'")
.and_return(double("git", success?: false, text_output: "fatal: couldn't find remote ref refs/notes/gotsha"))

expect(described_class.new.call).to eq("fetched")
expect(described_class.new.call).to eq("fetched")
end
end
end
end
37 changes: 29 additions & 8 deletions spec/gotsha/actions/push_spec.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
# frozen_string_literal: true

RSpec.describe Gotsha::Actions::Push do
let(:git_push_result) { double("git", success?: true) }

describe "push" do
it "force pushes Git notes" do
expect(Gotsha::BashCommand)
.to receive(:silent_run!)
.with("git push --no-verify --force origin refs/notes/gotsha:refs/notes/gotsha")
.and_return(git_push_result)
let(:git_push_result) { double("git", success?: true) }

before do
allow(Gotsha::RemoteResolver).to receive(:resolve).and_return(remote_name)
end

context "with default remote" do
let(:remote_name) { "origin" }

it "force pushes Git notes" do
expect(Gotsha::BashCommand)
.to receive(:silent_run!)
.with("git push --no-verify --force origin refs/notes/gotsha:refs/notes/gotsha")
.and_return(git_push_result)

expect(described_class.new.call).to eq("pushed")
end
end

context "with custom remote" do
let(:remote_name) { "upstream" }

it "uses the resolved remote" do
expect(Gotsha::BashCommand)
.to receive(:silent_run!)
.with("git push --no-verify --force upstream refs/notes/gotsha:refs/notes/gotsha")
.and_return(git_push_result)

expect(described_class.new.call).to eq("pushed")
expect(described_class.new.call).to eq("pushed")
end
end
end
end
6 changes: 6 additions & 0 deletions spec/gotsha/actions/uninstall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
RSpec.describe Gotsha::Actions::Uninstall do
describe "uninstall" do
it "removes config files and Git configuration" do
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with(Gotsha::Config::CONFIG_DIR).and_return(true)
allow(File).to receive(:exist?).with(Gotsha::Config::GH_CONFIG_FILE).and_return(true)
allow(File).to receive(:exist?).with(Gotsha::Config::GL_CONFIG_FILE).and_return(true)

expect(FileUtils).to receive(:rm_rf).with(Gotsha::Config::CONFIG_DIR)
expect(FileUtils).to receive(:rm).with(Gotsha::Config::GH_CONFIG_FILE)
expect(FileUtils).to receive(:rm).with(Gotsha::Config::GL_CONFIG_FILE)
expect(Gotsha::BashCommand).to receive(:silent_run!).with("git config --unset core.hooksPath")

described_class.new.call
Expand Down
Loading
Loading