Module: ChangeAgent::Sync

Included in:
Client
Defined in:
lib/change_agent/sync.rb

Defined Under Namespace

Classes: MergeConflict, MissingRemote

Constant Summary collapse

DEFAULT_REMOTE =
'origin'
DEFAULT_REMOTE_BRANCH =
'origin/master'
DEFAULT_LOCAL_REF =
'refs/heads/master'

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#credentialsObject

Default to token-based credentials passed as GITHUB_TOKEN Can be over ridden by overwritting @credentials with a different Rugged Credentialing method



17
18
19
20
21
22
# File 'lib/change_agent/sync.rb', line 17

def credentials
  @credentials ||= Rugged::Credentials::UserPassword.new({
                                                           username: 'x-oauth-basic',
                                                           password: ENV.fetch('GITHUB_TOKEN', nil)
                                                         })
end

Instance Method Details

#add_remote(name, url) ⇒ Object

Helper method to simplify adding a remote



30
31
32
# File 'lib/change_agent/sync.rb', line 30

def add_remote(name, url)
  remotes.create name, url
end

#fetch(remote = nil) ⇒ Object

Fetch a remote

Options:

remote - the name of the remote (default: origin)

Raises:



56
57
58
59
60
# File 'lib/change_agent/sync.rb', line 56

def fetch(remote = nil)
  raise MissingRemote unless has_remotes?

  repo.fetch(remote || DEFAULT_REMOTE, credentials: credentials)
end

#has_remotes?Boolean

Does the current repo have at least a single remote?

Returns:

  • (Boolean)


35
36
37
# File 'lib/change_agent/sync.rb', line 35

def has_remotes?
  remotes.count.positive?
end

#merge(options = {}) ⇒ Object

Merge two refs

Options:

:from   - the remote ref (default: "origin/master")
:to     - the local ref  (default: "refs/heads/master")

Raises:



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/change_agent/sync.rb', line 67

def merge(options = {})
  options[:from] = DEFAULT_REMOTE_BRANCH
  options[:to] = DEFAULT_LOCAL_REF
  theirs = repo.rev_parse options[:from]
  ours = repo.rev_parse options[:to]

  analysis = repo.merge_analysis(theirs)
  return analysis if analysis.include? :up_to_date

  base = repo.rev_parse(repo.merge_base(ours, theirs))
  index = ours.tree.merge(theirs.tree, base.tree)

  raise MergeConflict if index.conflicts?

  Rugged::Commit.create(repo, {
                          parents: [ours, theirs],
                          tree: index.write_tree(repo),
                          message: "Merged `#{options[:from]}` into `#{options[:to].sub('refs/heads/', '')}`",
                          update_ref: options[:to]
                        })
end

#pull(options = {}) ⇒ Object

Fetch a remote and merge

Options:

:remote - the name of the remote (default: origin)
:from   - the remote ref (default: "origin/master")
:to     - the local ref  (default: "refs/heads/master")


95
96
97
98
# File 'lib/change_agent/sync.rb', line 95

def pull(options = {})
  fetch(options[:remote])
  merge(options)
end

#push(options = {}) ⇒ Object

Push to a remote

Options:

:remote - the name of the remote (default: origin)
:ref    - the ref to push (default: "refs/heads/master")

Raises:



44
45
46
47
48
49
50
# File 'lib/change_agent/sync.rb', line 44

def push(options = {})
  raise MissingRemote unless has_remotes?

  options[:remote] = DEFAULT_REMOTE
  options[:ref] = DEFAULT_LOCAL_REF
  remotes[options[:remote]].push([options[:ref]], { credentials: credentials })
end

#remotesObject

Helper method to return all remots



25
26
27
# File 'lib/change_agent/sync.rb', line 25

def remotes
  repo.remotes
end

#syncObject

Perform both a pull and a push

Will fail if any conflicts occur



103
104
105
# File 'lib/change_agent/sync.rb', line 103

def sync
  pull && push
end