Class: MendixBridge::GitWorkflow

Inherits:
Object
  • Object
show all
Defined in:
lib/mendix_bridge/git_workflow.rb

Constant Summary collapse

IN_PROGRESS_MARKERS =
%w[
  MERGE_HEAD CHERRY_PICK_HEAD REVERT_HEAD REBASE_HEAD
  rebase-merge rebase-apply BISECT_LOG
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project_file, inventory_dir: nil, mxcli: nil, mx: nil) ⇒ GitWorkflow

Returns a new instance of GitWorkflow.



16
17
18
19
20
21
22
23
24
25
# File 'lib/mendix_bridge/git_workflow.rb', line 16

def initialize(project_file, inventory_dir: nil, mxcli: nil, mx: nil)
  @project_file = File.expand_path(project_file)
  @project_dir = File.dirname(@project_file)
  @inventory_dir = inventory_dir && File.expand_path(inventory_dir)
  @root = git!("rev-parse", "--show-toplevel").strip
  @git_dir = git!("rev-parse", "--absolute-git-dir").strip
  @mxcli = mxcli
  @mx = mx || resolve_mx
  verify_project_tracking!
end

Instance Attribute Details

#rootObject (readonly)

Returns the value of attribute root.



27
28
29
# File 'lib/mendix_bridge/git_workflow.rb', line 27

def root
  @root
end

Instance Method Details

#branchesObject



41
42
43
44
# File 'lib/mendix_bridge/git_workflow.rb', line 41

def branches
  git!("for-each-ref", "--format=%(refname:short)", "refs/heads", "refs/remotes")
    .lines.map(&:strip).reject { |name| name.end_with?("/HEAD") }
end

#commit(message, studio_closed:) ⇒ Object

Stages the Mendix project directory and commits it. Deliberately skips the mx check consistency validation so work-in-progress (even inconsistent) state can be committed; branch switches still guard against a dirty tree.

Raises:



174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/mendix_bridge/git_workflow.rb', line 174

def commit(message, studio_closed:)
  ensure_studio_closed!(studio_closed)
  ensure_no_operation!
  message = message.to_s.strip
  raise GitWorkflowError, "commit message cannot be empty" if message.empty?
  verify_project_tracking!

  git!("add", "--", project_pathspec)
  raise GitWorkflowError, "nothing to commit; the project has no staged changes" if
    git!("diff", "--cached", "--name-only").strip.empty?

  git!("commit", "-m", message)
  status
end

#create(branch, studio_closed:) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/mendix_bridge/git_workflow.rb', line 70

def create(branch, studio_closed:)
  ensure_switch_ready!(branch, studio_closed:)
  raise GitWorkflowError, "branch already exists: #{branch}" if local_branch?(branch) || remote_branch?(branch)

  previous = current_branch
  switched = false
  git!("switch", "-c", branch)
  switched = true
  validate_and_refresh!
  status
rescue StandardError => error
  rollback(previous) if switched
  raise error
end

#fetchObject



46
47
48
# File 'lib/mendix_bridge/git_workflow.rb', line 46

def fetch
  git!("fetch", "--prune", "origin")
end

#merge(branch, studio_closed:) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/mendix_bridge/git_workflow.rb', line 130

def merge(branch, studio_closed:)
  ensure_switch_ready!(branch, studio_closed:)
  output, error, result = Open3.capture3(
    "git", "-C", @root, "merge", "--no-ff", "--no-commit", branch
  )
  unless result.success?
    raise GitWorkflowError,
      "merge has conflicts; resolve them or run git merge --abort:\n#{error}#{output}"
  end

  begin
    validate_project!
  rescue StandardError => validation_error
    raise GitWorkflowError,
      "#{validation_error.message}\nmerge was not committed; run git merge --abort"
  end

  git!("commit", "--no-edit")
  refresh_inventory! if @inventory_dir
  status
end

#rebase(branch, studio_closed:) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/mendix_bridge/git_workflow.rb', line 152

def rebase(branch, studio_closed:)
  ensure_switch_ready!(branch, studio_closed:)
  validation = [
    Shellwords.escape(@mx),
    "check",
    Shellwords.escape(@project_file)
  ].join(" ")
  output, error, result = Open3.capture3(
    "git", "-C", @root, "rebase", "--exec", validation, branch
  )
  unless result.success?
    raise GitWorkflowError,
      "rebase stopped; resolve the issue and continue, or run git rebase --abort:\n#{error}#{output}"
  end

  refresh_inventory! if @inventory_dir
  status
end

#stash_apply(reference = "stash@{0}", studio_closed:, drop: false) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/mendix_bridge/git_workflow.rb', line 104

def stash_apply(reference = "stash@{0}", studio_closed:, drop: false)
  ensure_switch_ready!(current_branch, studio_closed:)
  output, error, result = Open3.capture3(
    "git", "-C", @root, "stash", "apply", "--index", reference
  )
  unless result.success?
    raise GitWorkflowError,
      "stash apply has conflicts; the stash was preserved:\n#{error}#{output}"
  end

  begin
    validate_project!
    refresh_inventory! if @inventory_dir
  rescue StandardError => validation_error
    raise GitWorkflowError,
      "#{validation_error.message}\nthe stash was preserved and its changes remain in the working tree"
  end

  git!("stash", "drop", reference) if drop
  status
end

#stash_drop(reference = "stash@{0}") ⇒ Object



126
127
128
# File 'lib/mendix_bridge/git_workflow.rb', line 126

def stash_drop(reference = "stash@{0}")
  git!("stash", "drop", reference).strip
end

#stash_listObject



96
97
98
# File 'lib/mendix_bridge/git_workflow.rb', line 96

def stash_list
  git!("stash", "list")
end

#stash_push(studio_closed:, message: nil, include_untracked: false) ⇒ Object



85
86
87
88
89
90
91
92
93
94
# File 'lib/mendix_bridge/git_workflow.rb', line 85

def stash_push(studio_closed:, message: nil, include_untracked: false)
  ensure_studio_closed!(studio_closed)
  ensure_no_operation!

  arguments = ["stash", "push"]
  arguments << "--include-untracked" if include_untracked
  arguments.concat(["-m", message]) if message
  output = git!(*arguments)
  { "output" => output.strip, **status }
end

#stash_show(reference = "stash@{0}") ⇒ Object



100
101
102
# File 'lib/mendix_bridge/git_workflow.rb', line 100

def stash_show(reference = "stash@{0}")
  git!("stash", "show", "--stat", reference)
end

#statusObject



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/mendix_bridge/git_workflow.rb', line 29

def status
  {
    "branch" => current_branch,
    "clean" => clean?,
    "operation_in_progress" => operation_in_progress,
    "project" => @project_file,
    "project_tracked" => tracked?(@project_file),
    "mprcontents_tracked" => mprcontents_tracked?,
    "ready_to_switch" => clean? && operation_in_progress.nil?
  }
end

#switch(branch, studio_closed:) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/mendix_bridge/git_workflow.rb', line 50

def switch(branch, studio_closed:)
  ensure_switch_ready!(branch, studio_closed:)
  previous = current_branch
  switched = false

  if local_branch?(branch)
    git!("switch", branch)
  elsif remote_branch?(branch)
    git!("switch", "--track", "-c", branch, "origin/#{branch}")
  else
    raise GitWorkflowError, "branch does not exist locally or at origin: #{branch}"
  end
  switched = true
  validate_and_refresh!
  status
rescue StandardError => error
  rollback(previous) if switched
  raise error
end