Class: SafeDb::GitFlow

Inherits:
Object
  • Object
show all
Defined in:
lib/utils/git/gitflow.rb

Overview

Provision the git branch involved in our present working directory. The [present directory] may not relate to version control at all or it may relate to the master or other branch in the source mgt tool.

Class Method Summary collapse

Class Method Details

.add_file(repo_path, file_path) ⇒ Object

Add a specific file that exists in the working copy to the git version controller.

Parameters:

  • repo_path (String)

    folder path to the desired git repository

  • file_path (String)

    file to add to the git version controller



145
146
147
148
149
150
151
152
153
# File 'lib/utils/git/gitflow.rb', line 145

def self.add_file( repo_path, file_path )

    path_to_dot_git = File.join( repo_path, ".git" )
    git_add_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} add #{file_path}"
    log.info(x) { "[git] single file add command => #{git_add_cmd}" }
    %x[#{git_add_cmd}];
    log.info(x) { "[git] has added #{file_path} into the git repository." }

end

.add_origin_url(repo_path, origin_url) ⇒ Object

Add the parameter remote origin URL to the git repository at the stated path. This method assumes the origin url is non-sensitive and logs it. No passwords or access tokens expected in the url.

Use in conjunction with set_push_origin_url to create a push url that is different from the fetch url.

Parameters:

  • repo_path (String)

    folder path to the desired git repository

  • origin_url (String)

    the URL to the remote origin to add



167
168
169
170
171
172
173
174
175
176
# File 'lib/utils/git/gitflow.rb', line 167

def self.add_origin_url repo_path, origin_url

    path_to_dot_git = File.join( repo_path, ".git" )
    git_add_origin_loggable_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} remote add origin"
    git_add_origin_cmd = "#{git_add_origin_loggable_cmd} #{origin_url}"
    log.info(x) { "[git] add origin command without url => #{git_add_origin_loggable_cmd}" }
    %x[#{git_add_origin_cmd}];
    log.info(x) { "[git] has added a remote origin url." }

end

.commit(repo_path, commit_msg) ⇒ Object

Commit all changes to the local git repo at the stated folder path with the parameter commit message.

Parameters:

  • repo_path (String)

    folder path to the desired git repository

  • commit_msg (String)

    the commit message to post to the repo



85
86
87
88
89
90
91
92
93
94
# File 'lib/utils/git/gitflow.rb', line 85

def self.commit( repo_path, commit_msg )

    log.info(x) { "[git] commit msg => #{commit_msg}" }
    path_to_dot_git = File.join( repo_path, ".git" )
    git_commit_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} commit -m \"#{commit_msg}\";"
    log.info(x) { "[git] commit command => #{git_commit_cmd}" }
    %x[#{git_commit_cmd}];
    log.info(x) { "[git] has committed resources into the local repository." }

end

.config(repo_path, user_email, user_name) ⇒ Object

Configure the user.email and user.name properties for the git software to use alongside its other commands. This must always be done before a git commit command is issued.

Parameters:

  • repo_path (String)

    folder path to the desired git repository

  • user_email (String)

    the email address of the user owning the repository

  • user_name (String)

    the full name of the user owning the repository



105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/utils/git/gitflow.rb', line 105

def self.config( repo_path, user_email, user_name )

    log.info(x) { "[git] local config for user.email => #{user_email}" }
    log.info(x) { "[git] local config for user.name => #{user_name}" }
    path_to_dot_git = File.join( repo_path, ".git" )
    git_config_email_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} config --local user.email \"#{user_email}\";"
    git_config_name_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} config --local user.name \"#{user_name}\";"
    log.info(x) { "[git] configure user.email command => #{git_config_email_cmd}" }
    log.info(x) { "[git] configure user.name command => #{git_config_name_cmd}" }
    %x[#{git_config_email_cmd}];
    %x[#{git_config_name_cmd}];
    log.info(x) { "[git] has locally configured the user.email and user.name properties." }

end

.del_file(repo_path, file_path) ⇒ Object

Remove a specific file from git management and also delete the working copy version of the file.

Parameters:

  • repo_path (String)

    folder path to the desired git repository

  • file_path (String)

    file to remove from the repo and working copy



127
128
129
130
131
132
133
134
135
# File 'lib/utils/git/gitflow.rb', line 127

def self.del_file( repo_path, file_path )

    path_to_dot_git = File.join( repo_path, ".git" )
    git_rm_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} rm #{file_path}"
    log.info(x) { "[git] file remove command => #{git_rm_cmd}" }
    %x[#{git_rm_cmd}];
    log.info(x) { "[git] has removed #{file_path} from repo and working copy." }

end

.do_clone_repo(repo_url, non_existent_path) ⇒ Object

– – Clone a remote repository at the specified [url] into – a [NON-EXISTENT] folder path. – – ——————————— – What is a Non Existent Dir Path? – ——————————— – – The parent directory of a non existent folder path – must [exist] whilst the full path itself does not. – The clone operation will create the final folder in – the path and then it [puts] the repository contents – within it. – – ———– – Parameters – ———– – – repo_url : url ends in dot git f-slash – clone_dir : path to new non-existent dir – – —————————– – Dependencies and Assumptions – —————————– – – git is installed on the machine – repo exists and is publicly readable – the master branch is he one to clone – the current Dir.pwd() is writeable –



405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/utils/git/gitflow.rb', line 405

def self.do_clone_repo repo_url, non_existent_path

  cmd = "git clone #{repo_url} #{non_existent_path}"
  clone_output = %x[#{cmd}];

  log.info(x) { "[gitflow] cloning remote repository" }
  log.info(x) { "[gitflow] git repository url : #{repo_url}" }
  log.info(x) { "[gitflow] git clone dir path : #{nickname non_existent_path}" }
  log.info(x) { "[gitflow] git clone command  : #{cmd}" }
  log.info(x) { "[gitflow] git clone output   : #{clone_output}" }

end

.do_clone_repos(repo_urls, base_names, parent_dir) ⇒ Object

– —————————————————– – # – Clone [many] git repositories given an array of urls – # – along with a corresponding array of the working copy – # – folder names and a [parental] base (offset) folder. – # – —————————————————– – # – Parameter – # – repo_urls : array of git repository urls – # – base_names : array of cloned repo base names – # – parent_dir : path to local [parent] folder – # – – # – Dependencies and Assumptions – # – arrays have equiv corresponding entries – # – parent dir is created if not exists – # – repos exist and are publicly readable – # – master branches are the ones to clone – # – —————————————————– – #



435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
# File 'lib/utils/git/gitflow.rb', line 435

def self.do_clone_repos repo_urls, base_names, parent_dir

  Dir.mkdir parent_dir unless File.exists? parent_dir
  Throw.if_not_found parent_dir, "clone repos"

  repo_urls.each_with_index do | repo_url, repo_index |

    git_url = repo_url if repo_url.end_with? @@url_postfix
    git_url = "#{repo_url}#{@@url_postfix}" unless repo_url.end_with? @@url_postfix

    proj_folder = File.join parent_dir, base_names[repo_index]

    log.info(x) { "[clone repos] proj [index] => #{repo_index}" }
    log.info(x) { "[clone repos] repo url 1st => #{repo_url}" }
    log.info(x) { "[clone repos] repo url 2nd => #{git_url}" }
    log.info(x) { "[clone repos] project name => #{base_names[repo_index]}" }
    log.info(x) { "[clone repos] project path => #{proj_folder}" }

    GitFlow.do_clone_repo git_url, proj_folder

  end

end

.do_clone_wc(path_to_dot_git, path_to_new_dir) ⇒ Object

– ————————————————– – # – Clone the branch of a local git repo working copy. – # – ————————————————– – # – Parameter – # – src_gitpath : local path to .git folder – # – new_wc_path : path to new non-existent dir – # – – # – Dependencies and Assumptions – # – git is installed on the machine – # – working copy exists and has remote origin – # – ————————————————– – #



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/utils/git/gitflow.rb', line 338

def self.do_clone_wc path_to_dot_git, path_to_new_dir

  # -- ----------------------------------------------------------- -- #
  # -- Why clone from a working copy (instead of a remote url).    -- #
  # -- ----------------------------------------------------------- -- #
  # --                                                             -- #
  # -- When actively [DEVELOPING] an eco plugin and you want to    -- #
  # --                                                             -- #
  # --   1 - [test] the behaviour without a git commit/git push    -- #
  # --   2 - test whatever [branch] the working copy is now at     -- #
  # --                                                             -- #
  # -- This use case requires us to clone from a working copy.     -- #
  # --                                                             -- #
  # -- ----------------------------------------------------------- -- #

###   Bug here - see getting branch name issue
###   Bug here - see getting branch name issue
###   Bug here - see getting branch name issue
###   Bug here - see getting branch name issue
###    branch_name = wc_branch_name path_to_dot_git
  branch_name = "master"
#####    cmd = "git clone #{path_to_dot_git} -b #{branch_name} #{path_to_new_dir}"
#####    cmd = "git clone #{path_to_dot_git} -b #{branch_name} #{path_to_new_dir}"
#####    cmd = "git clone #{path_to_dot_git} -b #{branch_name} #{path_to_new_dir}"
  cmd = "git clone #{path_to_dot_git} #{path_to_new_dir}"
  clone_output = %x[#{cmd}];

  log.info(x) { "[gitflow] cloning working copy" }
  log.info(x) { "[gitflow] repo branch name  : #{branch_name}" }
  log.info(x) { "[gitflow] src dot git path  : #{path_to_dot_git}" }
  log.info(x) { "[gitflow] new wc dir path   : #{path_to_new_dir}" }
  log.info(x) { "[gitflow] git clone command : #{cmd}" }
  log.info(x) { "[gitflow] git clone output  : #{clone_output}" }

end

.file_names(repo_url) ⇒ Object

– ————————————————- – # – Return an array of simple file names in the repo. – # – ————————————————- – # – Parameter – # – repo_url : the url of the repository to read – # – – # – Dependencies and Assumptions – # – we are not interested in folders – # – trawl is recursive (infinite depth) – # – git is installed on the machine – # – ————————————————- – #



529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
# File 'lib/utils/git/gitflow.rb', line 529

def self.file_names repo_url

  random_text = SecureRandom.urlsafe_base64(12).delete("-_").downcase
  cloned_name = "eco.repo.clone.#{random_text}"
  cloned_path = File.join Dir.tmpdir(), cloned_name

  do_clone_repo repo_url, cloned_path
  dot_git_path = File.join cloned_path, ".git"

  cmd = "git --git-dir=#{dot_git_path} ls-tree -r master --name-only"
  filename_lines = %x[#{cmd}];
  names_list = Array.new
  filename_lines.each_line do |line|
    names_list.push line.strip
  end

  log.info(x) { "[git2files] ----------------------------------------------" }
  log.info(x) { "[git2files] [#{names_list.length}] files in [#{repo_url}]" }
  log.info(x) { "[git2files] ----------------------------------------------" }
  log.info(x) { "[git2files] Random Text : #{random_text}" }
  log.info(x) { "[git2files] Cloned Name : #{cloned_name}" }
  log.info(x) { "[git2files] Cloned Path : #{cloned_path}" }
  log.info(x) { "[git2files] Repo Folder : #{dot_git_path}" }
  log.info(x) { "[git2files] Reading Cmd : #{cmd}" }
  log.info(x) { "[git2files] ----------------------------------------------" }
  pp names_list
  log.info(x) { "[git2files] ----------------------------------------------" }

  return names_list

end

.git2zip(repo_url, path_offset, target_dir, zip_basename) ⇒ Object

– ———————————————— – # – Move assets from a git repo to a local zip file. – # – ———————————————— – # – – # – Parameter – # – repo_url : the url of the git repository – # – path_offset : FWD-SLASH ENDED PATH in repo – # – target_dir : the target folder for new zip – # – zip_filename : extensionless name of the zip – # – – # – Return – # – path to the zip file created in a tmp folder – # – – # – ———————————————— – # – Dependencies and Assumptions – # – ———————————————— – # – – # – END PATH OFFSET WITH A FORWARD SLASH – # – IF NO OFFSET SEND “/” for path_offset – # – git is installed on the machine – # – the repo exists with path offset – # – the master branch is archived – # – name is unique as used to create a dir – # – – # – ———————————————— – #



485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
# File 'lib/utils/git/gitflow.rb', line 485

def self.git2zip repo_url, path_offset, target_dir, zip_basename

  log.info(x) { "[git2zip] ------------------------------------------- -- #" }
  log.info(x) { "[git2zip] archiving repo assets at path offset        -- #" }
  log.info(x) { "[git2zip] ------------------------------------------- -- #" }
  log.info(x) { "[git2zip] git repository url    : #{repo_url}" }
  log.info(x) { "[git2zip] slash tail dir offset : #{path_offset}" }
  log.info(x) { "[git2zip] target zip directory  : #{target_dir}" }
  log.info(x) { "[git2zip] zip file [base] name  : #{zip_basename}" }

  clone_dir = File.join Dir.tmpdir(), zip_basename
  do_clone_repo repo_url, clone_dir
  dot_git_path = File.join clone_dir, ".git"
  dst_zip_path = File.join target_dir, "#{zip_basename}.zip"

  the_offset = path_offset
  the_offset = "" if path_offset.length == 1
  cmd = "git --git-dir=#{dot_git_path} archive -o #{dst_zip_path} HEAD:#{the_offset}"
  clone_output = %x[#{cmd}];

  log.info(x) { "[git2zip] tmp clone src folder  : #{clone_dir}" }
  log.info(x) { "[git2zip] cloned dot git path   : #{dot_git_path}" }
  log.info(x) { "[git2zip] target zip full path  : #{dst_zip_path}" }
  log.info(x) { "[git2zip] git archive command   : #{cmd}" }
  log.info(x) { "[git2zip] ------------------------------------------- -- #" }
  log.info(x) { "#{clone_output}" }
  log.info(x) { "[git2zip] ------------------------------------------- -- #" }

  return dst_zip_path

end

.init(repo_path) ⇒ Object

Make the folder at the given path a git repository if it is not one already. If the folder is already under git management then this call has no effect.

Parameters:

  • repo_path (String)

    folder path to the desired git repository



17
18
19
20
21
22
23
24
25
26
# File 'lib/utils/git/gitflow.rb', line 17

def self.init repo_path

    git_init_cmd = "git init #{repo_path}"
    log.info(x) { "[git] add command => #{git_init_cmd}" }
    cmd_output = %x[#{git_init_cmd}];

    log.info(x) { "[git] initializing git repository at path #{repo_path}" }
    log.info(x) { "[git] init command output : #{cmd_output}" }

end

.list(repo_path, by_line = false) ⇒ Object

Log the files (names and/or content) that either do not come under the wing of git or have been added to git repository management but are yet to be committed into the repository. Also an files are logged that have either been updated, deleted or moved.

Parameters:

  • repo_path (String)

    folder path to the desired git repository

  • by_line (Boolean) (defaults to: false)

    if set to true the log will list the changed lines fronted either with a plus or a minus sign. False will just list the file names



39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/utils/git/gitflow.rb', line 39

def self.list( repo_path, =false )

    path_to_dot_git = File.join( repo_path, ".git" )
     =  ? "-v" : ""

    git_log_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} status #{}"
    log.info(x) { "[git] status command => #{git_log_cmd}" }
    git_log_output = %x[#{git_log_cmd}]

    git_log_output.log_debug if 
    git_log_output.log_info unless 

end

.push(repo_path) ⇒ Object

Push the commit bundles to the remote git repository.

Parameters:

  • repo_path (String)

    folder path to the desired git repository



205
206
207
208
209
210
211
212
213
# File 'lib/utils/git/gitflow.rb', line 205

def self.push repo_path

    path_to_dot_git = File.join( repo_path, ".git" )
    git_push_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} push origin master"
    log.info(x) { "[git] push command => #{git_push_cmd}" }
    system git_push_cmd
    log.info(x) { "[git] has pushed outstanding commit bundles to the remote backend repository." }

end

.set_push_origin_url(repo_path, push_origin_url) ⇒ Object

Set only the origin url to push to. This command leaves the fetch url as is. The push_origin_url is assumed sensitive as it may contain either passwords or access tokens. As such it is not logged.

The remote origin must be set before calling this method. If no origin is set this will throw a “no origin” error.

Parameters:

  • repo_path (String)

    folder path to the desired git repository

  • push_origin_url (String)

    the push URL of the remote origin



189
190
191
192
193
194
195
196
197
198
# File 'lib/utils/git/gitflow.rb', line 189

def self.set_push_origin_url repo_path, push_origin_url

    path_to_dot_git = File.join( repo_path, ".git" )
    git_loggable_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} remote set-url --push origin"
    git_set_push_origin_url_cmd = "#{git_loggable_cmd} #{push_origin_url}"
    log.info(x) { "[git] set push origin url command without url => #{git_loggable_cmd}" }
    %x[#{git_set_push_origin_url_cmd}];
    log.info(x) { "[git] has set the remote origin url for pushing." }

end

.stage(repo_path) ⇒ Object

Stage all files that evoke some kind of difference between the working copy and the local git repository so that they can all be committed.

Files that will be staged by this method can be

  • newly created files (that do not match gitignore patterns)

  • modified files that are already under git version management

  • files deleted in the working copy but not removed from the repository

  • files renamed in the same folder or with their path changed too

  • all the above but found recursively under the root repository path

Parameters:

  • repo_path (String)

    folder path to the desired git repository



68
69
70
71
72
73
74
75
76
# File 'lib/utils/git/gitflow.rb', line 68

def self.stage repo_path

    path_to_dot_git = File.join( repo_path, ".git" )
    git_add_cmd = "git --git-dir=#{path_to_dot_git} --work-tree=#{repo_path} add -A"
    log.info(x) { "[git] add command => #{git_add_cmd}" }
    %x[#{git_add_cmd}];
    log.info(x) { "[git] has recursively added resources to version management." }

end

.wc_branch_name(path_to_dot_git) ⇒ Object

– ————————————————- – # – Return the branch name of a local git repository. – # – ————————————————- – # – Parameter – # – path_to_dot_git : local path to the .git folder – # – – # – Dependencies and Assumptions – # – git is installed on the machine – # – ————————————————- – #



232
233
234
235
236
237
238
239
240
241
# File 'lib/utils/git/gitflow.rb', line 232

def self.wc_branch_name path_to_dot_git

  cmd = "git --git-dir=#{path_to_dot_git} branch";
  branch_names = %x[#{cmd}];
  branch_names.each_line do |line|
    return line[2, line.length].strip if line.start_with?('*')
  end
  raise ArgumentError.new "No branch name starts with asterix.\n#{cmd}\n#{branch_names}\n"

end

.wc_origin_url(path_to_dot_git) ⇒ Object

– ————————————————- – # – Get the remote origin url of a git working copy. – # – ————————————————- – # – Parameter – # – path_to_dot_git : local path to .git folder – # – – # – Dependencies and Assumptions – # – git is installed on the machine – # – working copy exists and has remote origin – # – ————————————————- – #



254
255
256
257
258
259
260
261
262
# File 'lib/utils/git/gitflow.rb', line 254

def self.wc_origin_url path_to_dot_git

  cmd = "git --git-dir=#{path_to_dot_git} config --get remote.origin.url"
  url = %x[#{cmd}];
  raise ArgumentError.new "No remote origin url.\n#{cmd}\n" if url.nil?
  
  return url.strip

end

.wc_revision(path_to_dot_git) ⇒ Object

– ————————————————– – # – Get brief revision of repo from working copy path. – # – ————————————————– – # – Parameter – # – path_to_dot_git : local path to .git folder – # – – # – Dependencies and Assumptions – # – we return the first 7 revision chars – # – git is installed on the machine – # – working copy exists and has remote origin – # – ————————————————– – #



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/utils/git/gitflow.rb', line 309

def self.wc_revision path_to_dot_git

  log.info(x) { "GitFlow path to dot git is => #{path_to_dot_git}" }
  Throw.if_not_exists path_to_dot_git

  uncut_revision = wc_revision_uncut path_to_dot_git
  log.info(x) { "GitFlow uncut full revision is => #{uncut_revision}" }

  # -- --------------------------------------------------------------------- -- #
  # -- Gits [short revision] hash has 7 chars. Note 4 is the usable minimum. -- #
  # -- For usage in stamps where space comes at a premium - 6 chars will do. -- #
  # -- --------------------------------------------------------------------- -- #
  ref_length = 7
  return "r" + uncut_revision[0..(ref_length - 1)];

end

.wc_revision_uncut(path_to_dot_git) ⇒ Object

– ————————————————– – # – Get the uncut revision of a git repo working copy. – # – ————————————————– – # – Parameter – # – path_to_dot_git : local path to .git folder – # – – # – Dependencies and Assumptions – # – git is installed on the machine – # – working copy exists and has remote origin – # – ————————————————– – #



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/utils/git/gitflow.rb', line 275

def self.wc_revision_uncut path_to_dot_git

  log.info(x) { "##### GitFlow path to dot git is => #{path_to_dot_git}" }
  repo_url = wc_origin_url path_to_dot_git
  log.info(x) { "##### The GitFlow repo url is => #{repo_url}" }

  ## Bug HERE - On Ubuntu the branch name is like => (HEAD detached at 067f9a3)
  ## Bug HERE - This creates a failure of => sh: 1: Syntax error: "(" unexpected
  ## Bug HERE - The unexpected failure occurs in the ls-remote command below
  ## Bug HERE - So hardcoding this to "master" for now
  # branch_name = wc_branch_name path_to_dot_git
  branch_name = "master"

  log.info(x) { "##### The GitFlow branch name is => #{branch_name}" }
  cmd = "git ls-remote #{repo_url} ls-remote -b #{branch_name}"
  log.info(x) { "##### The GitFlow get dirty rev command is => #{cmd}" }
  dirty_revision = %x[#{cmd}];
  log.info(x) { "##### The dirty revision is => #{dirty_revision}" }
  return dirty_revision.partition("refs/heads").first.strip;

end