gitt 1.0.0
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.adoc +134 -0
- data/README.adoc +299 -0
- data/gitt.gemspec +32 -0
- data/lib/gitt/commands/branch.rb +26 -0
- data/lib/gitt/commands/config.rb +33 -0
- data/lib/gitt/commands/log.rb +64 -0
- data/lib/gitt/commands/tag.rb +94 -0
- data/lib/gitt/models/commit.rb +40 -0
- data/lib/gitt/models/person.rb +17 -0
- data/lib/gitt/models/tag.rb +27 -0
- data/lib/gitt/models/trailer.rb +17 -0
- data/lib/gitt/parsers/attributer.rb +26 -0
- data/lib/gitt/parsers/commit.rb +72 -0
- data/lib/gitt/parsers/person.rb +27 -0
- data/lib/gitt/parsers/tag.rb +38 -0
- data/lib/gitt/parsers/trailer.rb +31 -0
- data/lib/gitt/repository.rb +67 -0
- data/lib/gitt/sanitizers/container.rb +15 -0
- data/lib/gitt/sanitizers/date.rb +7 -0
- data/lib/gitt/sanitizers/email.rb +7 -0
- data/lib/gitt/sanitizers/lines.rb +7 -0
- data/lib/gitt/sanitizers/paragraphs.rb +7 -0
- data/lib/gitt/sanitizers/scissors.rb +7 -0
- data/lib/gitt/sanitizers/signature.rb +19 -0
- data/lib/gitt/sanitizers/trailers.rb +18 -0
- data/lib/gitt/shared_contexts/git_commit.rb +16 -0
- data/lib/gitt/shared_contexts/git_repo.rb +29 -0
- data/lib/gitt/shared_contexts/temp_dir.rb +13 -0
- data/lib/gitt/shell.rb +25 -0
- data/lib/gitt.rb +16 -0
- data.tar.gz.sig +0 -0
- metadata +144 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads"
|
4
|
+
require "tempfile"
|
5
|
+
|
6
|
+
module Gitt
|
7
|
+
module Commands
|
8
|
+
# A Git tag command wrapper.
|
9
|
+
class Tag
|
10
|
+
include Dry::Monads[:result]
|
11
|
+
|
12
|
+
KEY_MAP = {
|
13
|
+
author_email: "%(*authoremail)",
|
14
|
+
author_name: "%(*authorname)",
|
15
|
+
authored_at: "%(*authordate:raw)",
|
16
|
+
authored_relative_at: "%(*authordate:relative)",
|
17
|
+
committed_at: "%(*committerdate:raw)",
|
18
|
+
committed_relative_at: "%(*committerdate:relative)",
|
19
|
+
committer_email: "%(*committeremail)",
|
20
|
+
committer_name: "%(*committername)",
|
21
|
+
body: "%(body)",
|
22
|
+
sha: "%(objectname)",
|
23
|
+
subject: "%(subject)",
|
24
|
+
version: "%(refname)"
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
def initialize shell: SHELL, key_map: KEY_MAP, parser: Parsers::Tag.new
|
28
|
+
@shell = shell
|
29
|
+
@key_map = key_map
|
30
|
+
@parser = parser
|
31
|
+
end
|
32
|
+
|
33
|
+
def call(*arguments) = shell.call "tag", *arguments
|
34
|
+
|
35
|
+
def create version, body = EMPTY_STRING, *flags
|
36
|
+
return Failure "Unable to create Git tag without version." unless version
|
37
|
+
return Failure "Tag exists: #{version}." if exist? version
|
38
|
+
|
39
|
+
Tempfile.open "git_plus" do |file|
|
40
|
+
file.write body
|
41
|
+
write version, file.tap(&:rewind), *flags
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def exist?(version) = local?(version) || remote?(version)
|
46
|
+
|
47
|
+
def index *arguments
|
48
|
+
arguments.prepend(pretty_format, "--list")
|
49
|
+
.then { |flags| call(*flags) }
|
50
|
+
.fmap { |content| String(content).scrub("?").split %("\n") }
|
51
|
+
.fmap { |entries| build_records entries }
|
52
|
+
end
|
53
|
+
|
54
|
+
def last
|
55
|
+
shell.call("describe", "--abbrev=0", "--tags", "--always")
|
56
|
+
.fmap(&:strip)
|
57
|
+
.or { |error| Failure error.delete_prefix("fatal: ").chomp }
|
58
|
+
end
|
59
|
+
|
60
|
+
def local?(version) = call("--list", version).value_or(EMPTY_STRING).match?(/\A#{version}\Z/)
|
61
|
+
|
62
|
+
def push = shell.call "push", "--tags"
|
63
|
+
|
64
|
+
def remote? version
|
65
|
+
shell.call("ls-remote", "--tags", "origin", version)
|
66
|
+
.value_or(EMPTY_STRING)
|
67
|
+
.match?(%r(.+tags/#{version}\Z))
|
68
|
+
end
|
69
|
+
|
70
|
+
def show version
|
71
|
+
call(pretty_format, "--list", version).fmap { |content| parser.call content }
|
72
|
+
end
|
73
|
+
|
74
|
+
def tagged? = !call.value_or(EMPTY_STRING).empty?
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
attr_reader :shell, :key_map, :parser
|
79
|
+
|
80
|
+
def pretty_format
|
81
|
+
key_map.reduce("") { |content, (key, value)| content + "<#{key}>#{value}</#{key}>%n" }
|
82
|
+
.then { |format| %(--format="#{format}") }
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_records(entries) = entries.map { |entry| parser.call entry }
|
86
|
+
|
87
|
+
def write version, file, *flags
|
88
|
+
arguments = ["--annotate", version, "--cleanup", "verbatim", *flags, "--file", file.path]
|
89
|
+
call(*arguments).fmap { version }
|
90
|
+
.or { Failure "Unable to create tag: #{version}." }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Models
|
5
|
+
# Represents commit details.
|
6
|
+
Commit = Struct.new(
|
7
|
+
:author_email,
|
8
|
+
:author_name,
|
9
|
+
:authored_at,
|
10
|
+
:authored_relative_at,
|
11
|
+
:body,
|
12
|
+
:body_lines,
|
13
|
+
:body_paragraphs,
|
14
|
+
:committed_at,
|
15
|
+
:committed_relative_at,
|
16
|
+
:committer_email,
|
17
|
+
:committer_name,
|
18
|
+
:lines,
|
19
|
+
:raw,
|
20
|
+
:sha,
|
21
|
+
:signature,
|
22
|
+
:subject,
|
23
|
+
:trailers,
|
24
|
+
keyword_init: true
|
25
|
+
) do
|
26
|
+
def initialize *arguments
|
27
|
+
super
|
28
|
+
freeze
|
29
|
+
end
|
30
|
+
|
31
|
+
def amend? = subject.match?(/\Aamend!\s/)
|
32
|
+
|
33
|
+
def fixup? = subject.match?(/\Afixup!\s/)
|
34
|
+
|
35
|
+
def squash? = subject.match?(/\Asquash!\s/)
|
36
|
+
|
37
|
+
def prefix? = amend? || fixup? || squash?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Models
|
5
|
+
# Represents a person within a repository.
|
6
|
+
Person = Struct.new :name, :delimiter, :email, keyword_init: true do
|
7
|
+
def self.for(string, parser: Parsers::Person.new) = parser.call string
|
8
|
+
|
9
|
+
def initialize *arguments
|
10
|
+
super
|
11
|
+
freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s = values.join
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Models
|
5
|
+
# Represents tag details.
|
6
|
+
Tag = Struct.new(
|
7
|
+
:author_email,
|
8
|
+
:author_name,
|
9
|
+
:authored_at,
|
10
|
+
:authored_relative_at,
|
11
|
+
:body,
|
12
|
+
:committed_at,
|
13
|
+
:committed_relative_at,
|
14
|
+
:committer_email,
|
15
|
+
:committer_name,
|
16
|
+
:sha,
|
17
|
+
:subject,
|
18
|
+
:version,
|
19
|
+
keyword_init: true
|
20
|
+
) do
|
21
|
+
def initialize *arguments
|
22
|
+
super
|
23
|
+
freeze
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Models
|
5
|
+
# Represents commit trailer details.
|
6
|
+
Trailer = Struct.new :key, :delimiter, :space, :value, keyword_init: true do
|
7
|
+
def self.for(string, parser: Parsers::Trailer.new) = parser.call string
|
8
|
+
|
9
|
+
def initialize *arguments
|
10
|
+
super
|
11
|
+
freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s = values.join
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Parsers
|
5
|
+
# Extracts attributes from XML formatted content.
|
6
|
+
class Attributer
|
7
|
+
def self.with(...) = new(...)
|
8
|
+
|
9
|
+
def initialize keys = []
|
10
|
+
@keys = keys
|
11
|
+
end
|
12
|
+
|
13
|
+
def call content
|
14
|
+
scrub = String(content).scrub "?"
|
15
|
+
|
16
|
+
keys.reduce({}) do |attributes, key|
|
17
|
+
attributes.merge key => scrub[%r(<#{key}>(?<value>.*?)</#{key}>)m, :value]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :keys
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "refinements/hashes"
|
4
|
+
|
5
|
+
module Gitt
|
6
|
+
module Parsers
|
7
|
+
# Parses raw commit information to produce a commit record.
|
8
|
+
class Commit
|
9
|
+
using Refinements::Hashes
|
10
|
+
|
11
|
+
def self.call(...) = new.call(...)
|
12
|
+
|
13
|
+
def initialize attributer: Attributer.with(Commands::Log::KEY_MAP.keys),
|
14
|
+
sanitizers: Sanitizers::CONTAINER,
|
15
|
+
model: Models::Commit
|
16
|
+
@attributer = attributer
|
17
|
+
@sanitizers = sanitizers
|
18
|
+
@model = model
|
19
|
+
end
|
20
|
+
|
21
|
+
def call content
|
22
|
+
attributer.call(content)
|
23
|
+
.then { |attributes| process attributes }
|
24
|
+
.then { |attributes| model[attributes] }
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :attributer, :sanitizers, :model
|
30
|
+
|
31
|
+
# :reek:TooManyStatements
|
32
|
+
def process attributes
|
33
|
+
body, trailers = attributes.values_at :body, :trailers
|
34
|
+
|
35
|
+
attributes.transform_with! body: scissors_sanitizer,
|
36
|
+
signature: signature_sanitizer,
|
37
|
+
trailers: trailers_sanitizer
|
38
|
+
|
39
|
+
attributes[:body] =
|
40
|
+
(trailers ? body.sub(/\n??#{Regexp.escape trailers}\n??/, "") : body).chomp
|
41
|
+
|
42
|
+
private_methods.grep(/\Aprocess_/).sort.each { |method| __send__ method, attributes }
|
43
|
+
attributes
|
44
|
+
end
|
45
|
+
|
46
|
+
# :reek:FeatureEnvy
|
47
|
+
def process_body_lines attributes
|
48
|
+
attributes[:body_lines] = lines_sanitizer.call attributes[:body]
|
49
|
+
end
|
50
|
+
|
51
|
+
# :reek:FeatureEnvy
|
52
|
+
def process_body_paragraphs attributes
|
53
|
+
attributes[:body_paragraphs] = paragraphs_sanitizer.call attributes[:body]
|
54
|
+
end
|
55
|
+
|
56
|
+
# :reek:FeatureEnvy
|
57
|
+
def process_lines attributes
|
58
|
+
attributes[:lines] = lines_sanitizer.call attributes[:raw]
|
59
|
+
end
|
60
|
+
|
61
|
+
def lines_sanitizer = sanitizers.fetch :lines
|
62
|
+
|
63
|
+
def paragraphs_sanitizer = sanitizers.fetch :paragraphs
|
64
|
+
|
65
|
+
def scissors_sanitizer = sanitizers.fetch :scissors
|
66
|
+
|
67
|
+
def signature_sanitizer = sanitizers.fetch :signature
|
68
|
+
|
69
|
+
def trailers_sanitizer = sanitizers.fetch :trailers
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Parsers
|
5
|
+
# Parses raw trailer data to produce a trailer record.
|
6
|
+
class Person
|
7
|
+
PATTERN = /
|
8
|
+
\A # Start of line.
|
9
|
+
(?<name>.*?) # Name (smallest possible).
|
10
|
+
(?<delimiter>\s?) # Space delimiter (optional).
|
11
|
+
(?<email><.+>)? # Collaborator email (optional).
|
12
|
+
\Z # End of line.
|
13
|
+
/x
|
14
|
+
|
15
|
+
def initialize model: Models::Person, pattern: PATTERN
|
16
|
+
@pattern = pattern
|
17
|
+
@model = model
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(content) = model[content.match(pattern).named_captures]
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :pattern, :model
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "refinements/hashes"
|
4
|
+
|
5
|
+
module Gitt
|
6
|
+
module Parsers
|
7
|
+
# Parses raw tag information to produce a tag record.
|
8
|
+
class Tag
|
9
|
+
using Refinements::Hashes
|
10
|
+
|
11
|
+
def initialize attributer: Attributer.with(Commands::Tag::KEY_MAP.keys),
|
12
|
+
sanitizers: Sanitizers::CONTAINER,
|
13
|
+
model: Models::Tag
|
14
|
+
@attributer = attributer
|
15
|
+
@sanitizers = sanitizers
|
16
|
+
@model = model
|
17
|
+
end
|
18
|
+
|
19
|
+
def call content
|
20
|
+
attributes = attributer.call content
|
21
|
+
attributes.transform_with! author_email: email_sanitizer,
|
22
|
+
authored_at: date_sanitizer,
|
23
|
+
committed_at: date_sanitizer,
|
24
|
+
committer_email: email_sanitizer,
|
25
|
+
version: -> value { value.delete_prefix "refs/tags/" if value }
|
26
|
+
model[attributes]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :attributer, :sanitizers, :model
|
32
|
+
|
33
|
+
def date_sanitizer = sanitizers.fetch(:date)
|
34
|
+
|
35
|
+
def email_sanitizer = sanitizers.fetch(:email)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Parsers
|
5
|
+
# Parses raw trailer data to produce a trailer record.
|
6
|
+
class Trailer
|
7
|
+
PATTERN = /
|
8
|
+
(?<key>\A.+) # Key (anchored to start of line).
|
9
|
+
(?<delimiter>:) # Colon delimiter.
|
10
|
+
(?<space>\s?) # Space (optional).
|
11
|
+
(?<value>.*?) # Value.
|
12
|
+
\Z # End of line.
|
13
|
+
/x
|
14
|
+
|
15
|
+
def initialize model: Models::Trailer, pattern: PATTERN
|
16
|
+
@pattern = pattern
|
17
|
+
@model = model
|
18
|
+
end
|
19
|
+
|
20
|
+
def call content
|
21
|
+
content.match(pattern)
|
22
|
+
.then { |data| data ? data.named_captures : {} }
|
23
|
+
.then { |attributes| model[attributes] }
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :pattern, :model
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
# Primary object/wrapper for processing all Git related commands.
|
5
|
+
# :reek:TooManyMethods
|
6
|
+
class Repository
|
7
|
+
COMMANDS = {
|
8
|
+
branch: Commands::Branch,
|
9
|
+
config: Commands::Config,
|
10
|
+
log: Commands::Log,
|
11
|
+
tag: Commands::Tag
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
def initialize shell: SHELL, commands: COMMANDS
|
15
|
+
@shell = shell
|
16
|
+
@commands = commands.transform_values { |command| command.new shell: }
|
17
|
+
end
|
18
|
+
|
19
|
+
def branch(...) = commands.fetch(__method__).call(...)
|
20
|
+
|
21
|
+
def branch_default = commands.fetch(:branch).default
|
22
|
+
|
23
|
+
def branch_name = commands.fetch(:branch).name
|
24
|
+
|
25
|
+
def call(...) = shell.call(...)
|
26
|
+
|
27
|
+
def commits(...) = commands.fetch(:log).index(...)
|
28
|
+
|
29
|
+
def config(...) = commands.fetch(__method__).call(...)
|
30
|
+
|
31
|
+
def exist? = shell.call("rev-parse", "--git-dir").value_or(EMPTY_STRING).chomp == ".git"
|
32
|
+
|
33
|
+
def get(...) = commands.fetch(:config).get(...)
|
34
|
+
|
35
|
+
def log(...) = commands.fetch(__method__).call(...)
|
36
|
+
|
37
|
+
def origin? = commands.fetch(:config).origin?
|
38
|
+
|
39
|
+
def set(...) = commands.fetch(:config).set(...)
|
40
|
+
|
41
|
+
def tag(...) = commands.fetch(__method__).call(...)
|
42
|
+
|
43
|
+
def tags(...) = commands.fetch(:tag).index(...)
|
44
|
+
|
45
|
+
def tag?(...) = commands.fetch(:tag).exist?(...)
|
46
|
+
|
47
|
+
def tag_create(...) = commands.fetch(:tag).create(...)
|
48
|
+
|
49
|
+
def tag_last = commands.fetch(:tag).last
|
50
|
+
|
51
|
+
def tag_local?(...) = commands.fetch(:tag).local?(...)
|
52
|
+
|
53
|
+
def tag_remote?(...) = commands.fetch(:tag).remote?(...)
|
54
|
+
|
55
|
+
def tag_show(...) = commands.fetch(:tag).show(...)
|
56
|
+
|
57
|
+
def tagged? = commands.fetch(:tag).tagged?
|
58
|
+
|
59
|
+
def tags_push = commands.fetch(:tag).push
|
60
|
+
|
61
|
+
def uncommitted(...) = commands.fetch(:log).uncommitted(...)
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
attr_reader :shell, :commands
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Sanitizers
|
5
|
+
CONTAINER = {
|
6
|
+
date: Date,
|
7
|
+
email: Email,
|
8
|
+
lines: Lines,
|
9
|
+
paragraphs: Paragraphs,
|
10
|
+
scissors: Scissors,
|
11
|
+
signature: Signature,
|
12
|
+
trailers: Trailers.new
|
13
|
+
}.freeze
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Sanitizers
|
5
|
+
Signature = lambda do |value|
|
6
|
+
case value
|
7
|
+
when "B" then "Bad"
|
8
|
+
when "E" then "Error"
|
9
|
+
when "G" then "Good"
|
10
|
+
when "N" then "None"
|
11
|
+
when "R" then "Revoked"
|
12
|
+
when "U" then "Unknown"
|
13
|
+
when "X" then "Expired"
|
14
|
+
when "Y" then "Expired Key"
|
15
|
+
else "Invalid"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitt
|
4
|
+
module Sanitizers
|
5
|
+
# Sanitizes content by turning it into an array of trailer records.
|
6
|
+
class Trailers
|
7
|
+
def initialize parser: Parsers::Trailer.new
|
8
|
+
@parser = parser
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(value) = String(value).split("\n").map { |text| parser.call text }
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
attr_reader :parser
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_context "with Git commit" do
|
4
|
+
let :git_commit do
|
5
|
+
Gitt::Models::Commit[
|
6
|
+
author_email: "test@example.com",
|
7
|
+
author_name: "Test User",
|
8
|
+
authored_relative_at: "1 day ago",
|
9
|
+
body: "",
|
10
|
+
raw: "",
|
11
|
+
sha: "180dec7d8ae8cbe3565a727c63c2111e49e0b737",
|
12
|
+
subject: "Added documentation",
|
13
|
+
trailers: ""
|
14
|
+
]
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_context "with Git repository" do
|
4
|
+
include_context "with temporary directory"
|
5
|
+
|
6
|
+
using Refinements::Pathnames
|
7
|
+
|
8
|
+
let(:git_repo_dir) { temp_dir.join "test" }
|
9
|
+
|
10
|
+
around do |example|
|
11
|
+
git_repo_dir.make_dir.change_dir do |path|
|
12
|
+
path.join("README.md").touch
|
13
|
+
|
14
|
+
`git init`
|
15
|
+
`git config user.name "Test User"`
|
16
|
+
`git config user.email "test@example.com"`
|
17
|
+
`git config core.hooksPath /dev/null`
|
18
|
+
`git config commit.gpgSign false`
|
19
|
+
`git config tag.gpgSign false`
|
20
|
+
`git config remote.origin.url https://github.com/bkuhlmann/test`
|
21
|
+
`git add --all .`
|
22
|
+
`git commit --all --message "Added documentation"`
|
23
|
+
end
|
24
|
+
|
25
|
+
example.run
|
26
|
+
|
27
|
+
temp_dir.remove_tree
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_context "with temporary directory" do
|
4
|
+
using Refinements::Pathnames
|
5
|
+
|
6
|
+
let(:temp_dir) { Bundler.root.join "tmp", "rspec" }
|
7
|
+
|
8
|
+
around do |example|
|
9
|
+
temp_dir.make_path
|
10
|
+
example.run
|
11
|
+
temp_dir.remove_tree
|
12
|
+
end
|
13
|
+
end
|
data/lib/gitt/shell.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads"
|
4
|
+
require "open3"
|
5
|
+
|
6
|
+
module Gitt
|
7
|
+
# A low-level shell client.
|
8
|
+
class Shell
|
9
|
+
include Dry::Monads[:result]
|
10
|
+
|
11
|
+
def initialize client: Open3
|
12
|
+
@client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(...)
|
16
|
+
client.capture3("git", ...).then do |stdout, stderr, status|
|
17
|
+
status.success? ? Success(stdout) : Failure(stderr)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :client
|
24
|
+
end
|
25
|
+
end
|
data/lib/gitt.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "zeitwerk"
|
4
|
+
|
5
|
+
Zeitwerk::Loader.for_gem.then do |loader|
|
6
|
+
loader.inflector.inflect "container" => "CONTAINER"
|
7
|
+
loader.ignore "#{__dir__}/gitt/shared_contexts"
|
8
|
+
loader.setup
|
9
|
+
end
|
10
|
+
|
11
|
+
# Main namespace.
|
12
|
+
module Gitt
|
13
|
+
EMPTY_STRING = ""
|
14
|
+
EMPTY_ARRAY = [].freeze
|
15
|
+
SHELL = Shell.new.freeze
|
16
|
+
end
|
data.tar.gz.sig
ADDED
Binary file
|