gitt 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|