Download a Git repo without .git folderBackup utility for local git repositoriesCounting the number of days...

When does coming up with an idea constitute sufficient contribution for authorship?

"On one hand" vs "on the one hand."

Why are the books in the Game of Thrones citadel library shelved spine inwards?

Called into a meeting and told we are being made redundant (laid off) and "not to share outside". Can I tell my partner?

Eww, those bytes are gross

Why zero tolerance on nudity in space?

Quenching swords in dragon blood; why?

The vanishing of sum of coefficients: symmetric polynomials

How to deal with an incendiary email that was recalled

Early credit roll before the end of the film

Can a hotel cancel a confirmed reservation?

Why avoid shared user accounts?

Issues with new Macs: Hardware makes them difficult for me to use. What options might be available in the future?

Pendulum Rotation

Avoiding morning and evening handshakes

Why would the Pakistan airspace closure cancel flights not headed to Pakistan itself?

What to do when being responsible for data protection in your lab, yet advice is ignored?

Are there any outlying considerations if I treat donning a shield as an object interaction during the first round of combat?

What's a good word to describe a public place that looks like it wouldn't be rough?

Strange Sign on Lab Door

How can I improve my fireworks photography?

what does conditioning on a random variable mean?

Do authors have to be politically correct in article-writing?

What is the wife of a henpecked husband called?



Download a Git repo without .git folder


Backup utility for local git repositoriesCounting the number of days worked for all commiters to a git repoRuby script to create git reposUse git to get SHA-1 of repoDetermining if a file or URL is a git repo or archiveGit tag release build files and reset to commit withoutAutomating the download of a GitHub repoGenerates a bare git repo from existing one and copy it to a central serverBash script to symlink configuration files from git repo into home directoryEnsure that a directory reflects a specified Git commit













3












$begingroup$


Coming from Python, JavaScript and PHP, I'd like to learn to write Ruby in the way it's "supposed to". Well-written Python code is called "Pythonic", so I'd like to know how idiomatic my Ruby code is.



I've had great help from Rubocop, slapping my face if I didn't write properly, but I still feel some things can be done better.



Application



This set of scripts downloads a github/gitlab/bitbucket repository, removes the .git folder and moves it to the specified folder, so that the files are "de-git".



Some commands:



# Run tests (for regexes)
./degit.rb --test

# Extract repo to folder with repo name
./degit.rb zpqrtbnk/test-repo

# Extract tag/branch of repo to folder with repo name
./degit.rb zpqrtbnk/test-repo#temp

# Extract repo to specified folder
./degit.rb zpqrtbnk/test-repo some-folder


Code



Ruby v2.4.5

Idea came from degit by Rich Harris



degit.rb



#!/usr/bin/env ruby

require "tmpdir"

require_relative "repo"
require_relative "repo_type"

REPO_TYPES = {
github: RepoType.new("github", "https://github.com", "github.com"),
gitlab: RepoType.new("gitlab", "https://gitlab.com", "gitlab.com"),
bitbucket: RepoType.new("bitbucket", "https://bitbucket.org", "bitbucket.org"),
custom: RepoType.new("custom", :custom, :custom),
}.freeze

# TODO: Error handling
def main
repo_name = ARGV[0]
folder_name = ARGV[1]
raise "Required parameter repo name not specified" if repo_name.nil?

if repo_name == "--test"
require_relative "tests"
run_tests
return
end

degit repo_name, folder_name
end

def temp_dir
dir = Dir.mktmpdir("degit-", "/tmp")
at_exit { FileUtils.remove_entry(dir) }
dir
end

def degit(repo_name, folder_name)
repo = Repo.new repo_name
folder_name ||= repo.name
dest_dir = File.join Dir.pwd, folder_name
dir_exists = Dir.exist? dest_dir
if dir_exists
abort "Aborted" unless confirm_overwrite dest_dir
end

dir = temp_dir
tmp_repo_path = File.join(dir, folder_name)
cmd = repo.download_command tmp_repo_path
puts `#{cmd}`
FileUtils.remove_entry File.join(tmp_repo_path, ".git")
FileUtils.remove_entry dest_dir if dir_exists
FileUtils.mv(tmp_repo_path, Dir.pwd, force: true)
end

def confirm_overwrite(dest_dir)
print "Destination folder #{dest_dir} already exists. Overwrite folder? [y/n] "
# ARGV interferes with gets, so use STDIN.gets
input = STDIN.gets.chomp.downcase
return (input == "y") if %w[y n].include? input

# Continue to ask until input is either y or n
confirm_overwrite dest_dir
end

main if $PROGRAM_NAME == __FILE__


repo_type.rb



class RepoType
attr_reader :name, :full_url

def initialize(name, full_url, base_url, short_code=nil)
@name = name
@full_url = full_url
@base_url = base_url
@short_code = short_code || name.to_s.downcase
end

def id?(id)
[@short_code, @base_url].include? id
end
end


repo.rb



class Repo
attr_reader :type, :tag, :name

PREFIX_REGEX = %r{
A
((?<type>github|gitlab|bitbucket):)?
(?<owner>[w-]+)/(?<name>[w-]+)
(#(?<tag>[w-.]+))?
z
}xi.freeze

SSH_REGEX = %r{
A
(?<source_url>
git@(?<type>github.com|gitlab.com|bitbucket.org):
(?<owner>[w-]+)/(?<name>[w-]+)
(.git)?
)
(#(?<tag>[w-.]+))?
z
}xi.freeze

HTTPS_REGEX = %r{
A
(?<source_url>
https://(?<type>github.com|gitlab.com|bitbucket.org)/
(?<owner>[w-]+)/(?<name>[w-]+)
)
(#(?<tag>[w-.]+))?
z
}xi.freeze

def initialize(uri)
@uri = uri

raise "Required constant REPO_TYPES not defined" unless defined? REPO_TYPES

parse_uri
# debug unless @source_url.nil?
end

def valid_uri?
@uri.end_with?(".git") || @uri.include?("/")
end

def parse_uri
if @uri.end_with? ".git"
@type = REPO_TYPES[:custom]
return
end

repo = match_repo_info
return nil if repo.nil?

@owner = repo[:owner]
@name = repo[:name]
@tag = repo[:tag]
@source_url = make_source_url repo
end

def match_repo_info
[PREFIX_REGEX, SSH_REGEX, HTTPS_REGEX].each do |regex|
repo_matches = regex.match @uri
unless repo_matches.nil?
@type = find_repo_type repo_matches[:type]
return repo_matches
end
end

nil
end

def find_repo_type(type)
REPO_TYPES.each do |_, repo_type|
return repo_type if repo_type.id? type
end

REPO_TYPES[:github]
end

def make_source_url(repo)
return repo[:source_url] if repo.names.include? "source_url"

source_url = @type.full_url || @uri
"#{source_url}/#{@owner}/#{@name}"
end

def download_command(output_folder=nil)
tag_spec = @tag.nil? ? "" : "--branch #{@tag}"
parts = [
"git clone --quiet --depth 1",
tag_spec,
@source_url,
output_folder || @name,
]
parts.join " "
end

def debug
puts ""
puts "source_url: #{@source_url}" unless @source_url.nil?
puts "owner: #{@owner}" unless @owner.nil?
puts "name: #{@name}" unless @name.nil?
puts "tag: #{@tag}" unless @tag.nil?
puts "download cmd: #{download_command}"
end
end


tests.rb



VALID = %w[
user1/repo1

github:user2/repo2
git@github.com:user3/repo3
https://github.com/rmccue/test-repository

gitlab:user5/repo5
git@gitlab.com:user6/repo6
https://gitlab.com/user7/repo7

bitbucket:user8/repo8
git@bitbucket.org:user9/repo9
https://bitbucket.org/user0/repo0
].freeze

INVALID = %w[
http://github.com/user1/repo1
https://github.com/user2
https://github.comuser3/repo3
].freeze

WITH_TAG = %w[
user1/repo1#dev
user2/repo2#v1.2.3
user3/repo3#1234abcd
].freeze

WITH_GIT_SUFFIX = %w[
https://github.com/Rich-Harris/degit.git
user@host:~/repos/website.nl.git
].freeze

def pf(str)
print str
$stdout.flush
end

def run_tests
pf " VALID: "
VALID.each do |r|
pf "."
repo = Repo.new r
raise "#{r} isn't valid" if repo.type.nil?
end
puts ""

pf " INVALID: "
INVALID.each do |r|
pf "."
repo = Repo.new r
raise "#{r} isn't invalid" unless repo.type.nil?
end
puts ""

pf " WITH_TAG: "
WITH_TAG.each do |r|
pf "."
repo = Repo.new r
raise "#{r} isn't valid" if repo.type.nil?
raise "#{r} has no tag" if repo.tag.nil?
end
puts ""

pf "WITH_GIT_SUFFIX: "
WITH_GIT_SUFFIX.each do |r|
pf "."
repo = Repo.new r
raise "#{r} isn't valid" if repo.type.nil?
end
puts ""
end









share|improve this question











$endgroup$

















    3












    $begingroup$


    Coming from Python, JavaScript and PHP, I'd like to learn to write Ruby in the way it's "supposed to". Well-written Python code is called "Pythonic", so I'd like to know how idiomatic my Ruby code is.



    I've had great help from Rubocop, slapping my face if I didn't write properly, but I still feel some things can be done better.



    Application



    This set of scripts downloads a github/gitlab/bitbucket repository, removes the .git folder and moves it to the specified folder, so that the files are "de-git".



    Some commands:



    # Run tests (for regexes)
    ./degit.rb --test

    # Extract repo to folder with repo name
    ./degit.rb zpqrtbnk/test-repo

    # Extract tag/branch of repo to folder with repo name
    ./degit.rb zpqrtbnk/test-repo#temp

    # Extract repo to specified folder
    ./degit.rb zpqrtbnk/test-repo some-folder


    Code



    Ruby v2.4.5

    Idea came from degit by Rich Harris



    degit.rb



    #!/usr/bin/env ruby

    require "tmpdir"

    require_relative "repo"
    require_relative "repo_type"

    REPO_TYPES = {
    github: RepoType.new("github", "https://github.com", "github.com"),
    gitlab: RepoType.new("gitlab", "https://gitlab.com", "gitlab.com"),
    bitbucket: RepoType.new("bitbucket", "https://bitbucket.org", "bitbucket.org"),
    custom: RepoType.new("custom", :custom, :custom),
    }.freeze

    # TODO: Error handling
    def main
    repo_name = ARGV[0]
    folder_name = ARGV[1]
    raise "Required parameter repo name not specified" if repo_name.nil?

    if repo_name == "--test"
    require_relative "tests"
    run_tests
    return
    end

    degit repo_name, folder_name
    end

    def temp_dir
    dir = Dir.mktmpdir("degit-", "/tmp")
    at_exit { FileUtils.remove_entry(dir) }
    dir
    end

    def degit(repo_name, folder_name)
    repo = Repo.new repo_name
    folder_name ||= repo.name
    dest_dir = File.join Dir.pwd, folder_name
    dir_exists = Dir.exist? dest_dir
    if dir_exists
    abort "Aborted" unless confirm_overwrite dest_dir
    end

    dir = temp_dir
    tmp_repo_path = File.join(dir, folder_name)
    cmd = repo.download_command tmp_repo_path
    puts `#{cmd}`
    FileUtils.remove_entry File.join(tmp_repo_path, ".git")
    FileUtils.remove_entry dest_dir if dir_exists
    FileUtils.mv(tmp_repo_path, Dir.pwd, force: true)
    end

    def confirm_overwrite(dest_dir)
    print "Destination folder #{dest_dir} already exists. Overwrite folder? [y/n] "
    # ARGV interferes with gets, so use STDIN.gets
    input = STDIN.gets.chomp.downcase
    return (input == "y") if %w[y n].include? input

    # Continue to ask until input is either y or n
    confirm_overwrite dest_dir
    end

    main if $PROGRAM_NAME == __FILE__


    repo_type.rb



    class RepoType
    attr_reader :name, :full_url

    def initialize(name, full_url, base_url, short_code=nil)
    @name = name
    @full_url = full_url
    @base_url = base_url
    @short_code = short_code || name.to_s.downcase
    end

    def id?(id)
    [@short_code, @base_url].include? id
    end
    end


    repo.rb



    class Repo
    attr_reader :type, :tag, :name

    PREFIX_REGEX = %r{
    A
    ((?<type>github|gitlab|bitbucket):)?
    (?<owner>[w-]+)/(?<name>[w-]+)
    (#(?<tag>[w-.]+))?
    z
    }xi.freeze

    SSH_REGEX = %r{
    A
    (?<source_url>
    git@(?<type>github.com|gitlab.com|bitbucket.org):
    (?<owner>[w-]+)/(?<name>[w-]+)
    (.git)?
    )
    (#(?<tag>[w-.]+))?
    z
    }xi.freeze

    HTTPS_REGEX = %r{
    A
    (?<source_url>
    https://(?<type>github.com|gitlab.com|bitbucket.org)/
    (?<owner>[w-]+)/(?<name>[w-]+)
    )
    (#(?<tag>[w-.]+))?
    z
    }xi.freeze

    def initialize(uri)
    @uri = uri

    raise "Required constant REPO_TYPES not defined" unless defined? REPO_TYPES

    parse_uri
    # debug unless @source_url.nil?
    end

    def valid_uri?
    @uri.end_with?(".git") || @uri.include?("/")
    end

    def parse_uri
    if @uri.end_with? ".git"
    @type = REPO_TYPES[:custom]
    return
    end

    repo = match_repo_info
    return nil if repo.nil?

    @owner = repo[:owner]
    @name = repo[:name]
    @tag = repo[:tag]
    @source_url = make_source_url repo
    end

    def match_repo_info
    [PREFIX_REGEX, SSH_REGEX, HTTPS_REGEX].each do |regex|
    repo_matches = regex.match @uri
    unless repo_matches.nil?
    @type = find_repo_type repo_matches[:type]
    return repo_matches
    end
    end

    nil
    end

    def find_repo_type(type)
    REPO_TYPES.each do |_, repo_type|
    return repo_type if repo_type.id? type
    end

    REPO_TYPES[:github]
    end

    def make_source_url(repo)
    return repo[:source_url] if repo.names.include? "source_url"

    source_url = @type.full_url || @uri
    "#{source_url}/#{@owner}/#{@name}"
    end

    def download_command(output_folder=nil)
    tag_spec = @tag.nil? ? "" : "--branch #{@tag}"
    parts = [
    "git clone --quiet --depth 1",
    tag_spec,
    @source_url,
    output_folder || @name,
    ]
    parts.join " "
    end

    def debug
    puts ""
    puts "source_url: #{@source_url}" unless @source_url.nil?
    puts "owner: #{@owner}" unless @owner.nil?
    puts "name: #{@name}" unless @name.nil?
    puts "tag: #{@tag}" unless @tag.nil?
    puts "download cmd: #{download_command}"
    end
    end


    tests.rb



    VALID = %w[
    user1/repo1

    github:user2/repo2
    git@github.com:user3/repo3
    https://github.com/rmccue/test-repository

    gitlab:user5/repo5
    git@gitlab.com:user6/repo6
    https://gitlab.com/user7/repo7

    bitbucket:user8/repo8
    git@bitbucket.org:user9/repo9
    https://bitbucket.org/user0/repo0
    ].freeze

    INVALID = %w[
    http://github.com/user1/repo1
    https://github.com/user2
    https://github.comuser3/repo3
    ].freeze

    WITH_TAG = %w[
    user1/repo1#dev
    user2/repo2#v1.2.3
    user3/repo3#1234abcd
    ].freeze

    WITH_GIT_SUFFIX = %w[
    https://github.com/Rich-Harris/degit.git
    user@host:~/repos/website.nl.git
    ].freeze

    def pf(str)
    print str
    $stdout.flush
    end

    def run_tests
    pf " VALID: "
    VALID.each do |r|
    pf "."
    repo = Repo.new r
    raise "#{r} isn't valid" if repo.type.nil?
    end
    puts ""

    pf " INVALID: "
    INVALID.each do |r|
    pf "."
    repo = Repo.new r
    raise "#{r} isn't invalid" unless repo.type.nil?
    end
    puts ""

    pf " WITH_TAG: "
    WITH_TAG.each do |r|
    pf "."
    repo = Repo.new r
    raise "#{r} isn't valid" if repo.type.nil?
    raise "#{r} has no tag" if repo.tag.nil?
    end
    puts ""

    pf "WITH_GIT_SUFFIX: "
    WITH_GIT_SUFFIX.each do |r|
    pf "."
    repo = Repo.new r
    raise "#{r} isn't valid" if repo.type.nil?
    end
    puts ""
    end









    share|improve this question











    $endgroup$















      3












      3








      3





      $begingroup$


      Coming from Python, JavaScript and PHP, I'd like to learn to write Ruby in the way it's "supposed to". Well-written Python code is called "Pythonic", so I'd like to know how idiomatic my Ruby code is.



      I've had great help from Rubocop, slapping my face if I didn't write properly, but I still feel some things can be done better.



      Application



      This set of scripts downloads a github/gitlab/bitbucket repository, removes the .git folder and moves it to the specified folder, so that the files are "de-git".



      Some commands:



      # Run tests (for regexes)
      ./degit.rb --test

      # Extract repo to folder with repo name
      ./degit.rb zpqrtbnk/test-repo

      # Extract tag/branch of repo to folder with repo name
      ./degit.rb zpqrtbnk/test-repo#temp

      # Extract repo to specified folder
      ./degit.rb zpqrtbnk/test-repo some-folder


      Code



      Ruby v2.4.5

      Idea came from degit by Rich Harris



      degit.rb



      #!/usr/bin/env ruby

      require "tmpdir"

      require_relative "repo"
      require_relative "repo_type"

      REPO_TYPES = {
      github: RepoType.new("github", "https://github.com", "github.com"),
      gitlab: RepoType.new("gitlab", "https://gitlab.com", "gitlab.com"),
      bitbucket: RepoType.new("bitbucket", "https://bitbucket.org", "bitbucket.org"),
      custom: RepoType.new("custom", :custom, :custom),
      }.freeze

      # TODO: Error handling
      def main
      repo_name = ARGV[0]
      folder_name = ARGV[1]
      raise "Required parameter repo name not specified" if repo_name.nil?

      if repo_name == "--test"
      require_relative "tests"
      run_tests
      return
      end

      degit repo_name, folder_name
      end

      def temp_dir
      dir = Dir.mktmpdir("degit-", "/tmp")
      at_exit { FileUtils.remove_entry(dir) }
      dir
      end

      def degit(repo_name, folder_name)
      repo = Repo.new repo_name
      folder_name ||= repo.name
      dest_dir = File.join Dir.pwd, folder_name
      dir_exists = Dir.exist? dest_dir
      if dir_exists
      abort "Aborted" unless confirm_overwrite dest_dir
      end

      dir = temp_dir
      tmp_repo_path = File.join(dir, folder_name)
      cmd = repo.download_command tmp_repo_path
      puts `#{cmd}`
      FileUtils.remove_entry File.join(tmp_repo_path, ".git")
      FileUtils.remove_entry dest_dir if dir_exists
      FileUtils.mv(tmp_repo_path, Dir.pwd, force: true)
      end

      def confirm_overwrite(dest_dir)
      print "Destination folder #{dest_dir} already exists. Overwrite folder? [y/n] "
      # ARGV interferes with gets, so use STDIN.gets
      input = STDIN.gets.chomp.downcase
      return (input == "y") if %w[y n].include? input

      # Continue to ask until input is either y or n
      confirm_overwrite dest_dir
      end

      main if $PROGRAM_NAME == __FILE__


      repo_type.rb



      class RepoType
      attr_reader :name, :full_url

      def initialize(name, full_url, base_url, short_code=nil)
      @name = name
      @full_url = full_url
      @base_url = base_url
      @short_code = short_code || name.to_s.downcase
      end

      def id?(id)
      [@short_code, @base_url].include? id
      end
      end


      repo.rb



      class Repo
      attr_reader :type, :tag, :name

      PREFIX_REGEX = %r{
      A
      ((?<type>github|gitlab|bitbucket):)?
      (?<owner>[w-]+)/(?<name>[w-]+)
      (#(?<tag>[w-.]+))?
      z
      }xi.freeze

      SSH_REGEX = %r{
      A
      (?<source_url>
      git@(?<type>github.com|gitlab.com|bitbucket.org):
      (?<owner>[w-]+)/(?<name>[w-]+)
      (.git)?
      )
      (#(?<tag>[w-.]+))?
      z
      }xi.freeze

      HTTPS_REGEX = %r{
      A
      (?<source_url>
      https://(?<type>github.com|gitlab.com|bitbucket.org)/
      (?<owner>[w-]+)/(?<name>[w-]+)
      )
      (#(?<tag>[w-.]+))?
      z
      }xi.freeze

      def initialize(uri)
      @uri = uri

      raise "Required constant REPO_TYPES not defined" unless defined? REPO_TYPES

      parse_uri
      # debug unless @source_url.nil?
      end

      def valid_uri?
      @uri.end_with?(".git") || @uri.include?("/")
      end

      def parse_uri
      if @uri.end_with? ".git"
      @type = REPO_TYPES[:custom]
      return
      end

      repo = match_repo_info
      return nil if repo.nil?

      @owner = repo[:owner]
      @name = repo[:name]
      @tag = repo[:tag]
      @source_url = make_source_url repo
      end

      def match_repo_info
      [PREFIX_REGEX, SSH_REGEX, HTTPS_REGEX].each do |regex|
      repo_matches = regex.match @uri
      unless repo_matches.nil?
      @type = find_repo_type repo_matches[:type]
      return repo_matches
      end
      end

      nil
      end

      def find_repo_type(type)
      REPO_TYPES.each do |_, repo_type|
      return repo_type if repo_type.id? type
      end

      REPO_TYPES[:github]
      end

      def make_source_url(repo)
      return repo[:source_url] if repo.names.include? "source_url"

      source_url = @type.full_url || @uri
      "#{source_url}/#{@owner}/#{@name}"
      end

      def download_command(output_folder=nil)
      tag_spec = @tag.nil? ? "" : "--branch #{@tag}"
      parts = [
      "git clone --quiet --depth 1",
      tag_spec,
      @source_url,
      output_folder || @name,
      ]
      parts.join " "
      end

      def debug
      puts ""
      puts "source_url: #{@source_url}" unless @source_url.nil?
      puts "owner: #{@owner}" unless @owner.nil?
      puts "name: #{@name}" unless @name.nil?
      puts "tag: #{@tag}" unless @tag.nil?
      puts "download cmd: #{download_command}"
      end
      end


      tests.rb



      VALID = %w[
      user1/repo1

      github:user2/repo2
      git@github.com:user3/repo3
      https://github.com/rmccue/test-repository

      gitlab:user5/repo5
      git@gitlab.com:user6/repo6
      https://gitlab.com/user7/repo7

      bitbucket:user8/repo8
      git@bitbucket.org:user9/repo9
      https://bitbucket.org/user0/repo0
      ].freeze

      INVALID = %w[
      http://github.com/user1/repo1
      https://github.com/user2
      https://github.comuser3/repo3
      ].freeze

      WITH_TAG = %w[
      user1/repo1#dev
      user2/repo2#v1.2.3
      user3/repo3#1234abcd
      ].freeze

      WITH_GIT_SUFFIX = %w[
      https://github.com/Rich-Harris/degit.git
      user@host:~/repos/website.nl.git
      ].freeze

      def pf(str)
      print str
      $stdout.flush
      end

      def run_tests
      pf " VALID: "
      VALID.each do |r|
      pf "."
      repo = Repo.new r
      raise "#{r} isn't valid" if repo.type.nil?
      end
      puts ""

      pf " INVALID: "
      INVALID.each do |r|
      pf "."
      repo = Repo.new r
      raise "#{r} isn't invalid" unless repo.type.nil?
      end
      puts ""

      pf " WITH_TAG: "
      WITH_TAG.each do |r|
      pf "."
      repo = Repo.new r
      raise "#{r} isn't valid" if repo.type.nil?
      raise "#{r} has no tag" if repo.tag.nil?
      end
      puts ""

      pf "WITH_GIT_SUFFIX: "
      WITH_GIT_SUFFIX.each do |r|
      pf "."
      repo = Repo.new r
      raise "#{r} isn't valid" if repo.type.nil?
      end
      puts ""
      end









      share|improve this question











      $endgroup$




      Coming from Python, JavaScript and PHP, I'd like to learn to write Ruby in the way it's "supposed to". Well-written Python code is called "Pythonic", so I'd like to know how idiomatic my Ruby code is.



      I've had great help from Rubocop, slapping my face if I didn't write properly, but I still feel some things can be done better.



      Application



      This set of scripts downloads a github/gitlab/bitbucket repository, removes the .git folder and moves it to the specified folder, so that the files are "de-git".



      Some commands:



      # Run tests (for regexes)
      ./degit.rb --test

      # Extract repo to folder with repo name
      ./degit.rb zpqrtbnk/test-repo

      # Extract tag/branch of repo to folder with repo name
      ./degit.rb zpqrtbnk/test-repo#temp

      # Extract repo to specified folder
      ./degit.rb zpqrtbnk/test-repo some-folder


      Code



      Ruby v2.4.5

      Idea came from degit by Rich Harris



      degit.rb



      #!/usr/bin/env ruby

      require "tmpdir"

      require_relative "repo"
      require_relative "repo_type"

      REPO_TYPES = {
      github: RepoType.new("github", "https://github.com", "github.com"),
      gitlab: RepoType.new("gitlab", "https://gitlab.com", "gitlab.com"),
      bitbucket: RepoType.new("bitbucket", "https://bitbucket.org", "bitbucket.org"),
      custom: RepoType.new("custom", :custom, :custom),
      }.freeze

      # TODO: Error handling
      def main
      repo_name = ARGV[0]
      folder_name = ARGV[1]
      raise "Required parameter repo name not specified" if repo_name.nil?

      if repo_name == "--test"
      require_relative "tests"
      run_tests
      return
      end

      degit repo_name, folder_name
      end

      def temp_dir
      dir = Dir.mktmpdir("degit-", "/tmp")
      at_exit { FileUtils.remove_entry(dir) }
      dir
      end

      def degit(repo_name, folder_name)
      repo = Repo.new repo_name
      folder_name ||= repo.name
      dest_dir = File.join Dir.pwd, folder_name
      dir_exists = Dir.exist? dest_dir
      if dir_exists
      abort "Aborted" unless confirm_overwrite dest_dir
      end

      dir = temp_dir
      tmp_repo_path = File.join(dir, folder_name)
      cmd = repo.download_command tmp_repo_path
      puts `#{cmd}`
      FileUtils.remove_entry File.join(tmp_repo_path, ".git")
      FileUtils.remove_entry dest_dir if dir_exists
      FileUtils.mv(tmp_repo_path, Dir.pwd, force: true)
      end

      def confirm_overwrite(dest_dir)
      print "Destination folder #{dest_dir} already exists. Overwrite folder? [y/n] "
      # ARGV interferes with gets, so use STDIN.gets
      input = STDIN.gets.chomp.downcase
      return (input == "y") if %w[y n].include? input

      # Continue to ask until input is either y or n
      confirm_overwrite dest_dir
      end

      main if $PROGRAM_NAME == __FILE__


      repo_type.rb



      class RepoType
      attr_reader :name, :full_url

      def initialize(name, full_url, base_url, short_code=nil)
      @name = name
      @full_url = full_url
      @base_url = base_url
      @short_code = short_code || name.to_s.downcase
      end

      def id?(id)
      [@short_code, @base_url].include? id
      end
      end


      repo.rb



      class Repo
      attr_reader :type, :tag, :name

      PREFIX_REGEX = %r{
      A
      ((?<type>github|gitlab|bitbucket):)?
      (?<owner>[w-]+)/(?<name>[w-]+)
      (#(?<tag>[w-.]+))?
      z
      }xi.freeze

      SSH_REGEX = %r{
      A
      (?<source_url>
      git@(?<type>github.com|gitlab.com|bitbucket.org):
      (?<owner>[w-]+)/(?<name>[w-]+)
      (.git)?
      )
      (#(?<tag>[w-.]+))?
      z
      }xi.freeze

      HTTPS_REGEX = %r{
      A
      (?<source_url>
      https://(?<type>github.com|gitlab.com|bitbucket.org)/
      (?<owner>[w-]+)/(?<name>[w-]+)
      )
      (#(?<tag>[w-.]+))?
      z
      }xi.freeze

      def initialize(uri)
      @uri = uri

      raise "Required constant REPO_TYPES not defined" unless defined? REPO_TYPES

      parse_uri
      # debug unless @source_url.nil?
      end

      def valid_uri?
      @uri.end_with?(".git") || @uri.include?("/")
      end

      def parse_uri
      if @uri.end_with? ".git"
      @type = REPO_TYPES[:custom]
      return
      end

      repo = match_repo_info
      return nil if repo.nil?

      @owner = repo[:owner]
      @name = repo[:name]
      @tag = repo[:tag]
      @source_url = make_source_url repo
      end

      def match_repo_info
      [PREFIX_REGEX, SSH_REGEX, HTTPS_REGEX].each do |regex|
      repo_matches = regex.match @uri
      unless repo_matches.nil?
      @type = find_repo_type repo_matches[:type]
      return repo_matches
      end
      end

      nil
      end

      def find_repo_type(type)
      REPO_TYPES.each do |_, repo_type|
      return repo_type if repo_type.id? type
      end

      REPO_TYPES[:github]
      end

      def make_source_url(repo)
      return repo[:source_url] if repo.names.include? "source_url"

      source_url = @type.full_url || @uri
      "#{source_url}/#{@owner}/#{@name}"
      end

      def download_command(output_folder=nil)
      tag_spec = @tag.nil? ? "" : "--branch #{@tag}"
      parts = [
      "git clone --quiet --depth 1",
      tag_spec,
      @source_url,
      output_folder || @name,
      ]
      parts.join " "
      end

      def debug
      puts ""
      puts "source_url: #{@source_url}" unless @source_url.nil?
      puts "owner: #{@owner}" unless @owner.nil?
      puts "name: #{@name}" unless @name.nil?
      puts "tag: #{@tag}" unless @tag.nil?
      puts "download cmd: #{download_command}"
      end
      end


      tests.rb



      VALID = %w[
      user1/repo1

      github:user2/repo2
      git@github.com:user3/repo3
      https://github.com/rmccue/test-repository

      gitlab:user5/repo5
      git@gitlab.com:user6/repo6
      https://gitlab.com/user7/repo7

      bitbucket:user8/repo8
      git@bitbucket.org:user9/repo9
      https://bitbucket.org/user0/repo0
      ].freeze

      INVALID = %w[
      http://github.com/user1/repo1
      https://github.com/user2
      https://github.comuser3/repo3
      ].freeze

      WITH_TAG = %w[
      user1/repo1#dev
      user2/repo2#v1.2.3
      user3/repo3#1234abcd
      ].freeze

      WITH_GIT_SUFFIX = %w[
      https://github.com/Rich-Harris/degit.git
      user@host:~/repos/website.nl.git
      ].freeze

      def pf(str)
      print str
      $stdout.flush
      end

      def run_tests
      pf " VALID: "
      VALID.each do |r|
      pf "."
      repo = Repo.new r
      raise "#{r} isn't valid" if repo.type.nil?
      end
      puts ""

      pf " INVALID: "
      INVALID.each do |r|
      pf "."
      repo = Repo.new r
      raise "#{r} isn't invalid" unless repo.type.nil?
      end
      puts ""

      pf " WITH_TAG: "
      WITH_TAG.each do |r|
      pf "."
      repo = Repo.new r
      raise "#{r} isn't valid" if repo.type.nil?
      raise "#{r} has no tag" if repo.tag.nil?
      end
      puts ""

      pf "WITH_GIT_SUFFIX: "
      WITH_GIT_SUFFIX.each do |r|
      pf "."
      repo = Repo.new r
      raise "#{r} isn't valid" if repo.type.nil?
      end
      puts ""
      end






      beginner ruby console git






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Feb 1 at 7:20







      Richard de Wit

















      asked Jan 31 at 15:28









      Richard de WitRichard de Wit

      1164




      1164






















          1 Answer
          1






          active

          oldest

          votes


















          0












          $begingroup$

          Ruby does not have an import system like python, so variables and methods at the top level like REPO_TYPES and temp_dir are effectively global variables and methods.



          Use modules to aggressively namespace, even for your main, especially when a small script begins to span more than one file:



          module Degit
          def self.main # define singleton method
          end
          end

          Degit.main # call singleton method


          This is also true for methods as well. def self.main in the example defines a singleton method on Degit itself. (Degit is a singleton in the sense that it will be the only instance of Module named "Degit", and main is a method it will now have).



          Ruby classes operate in the same way:



          class Foo
          class << self # opens singleton context
          def foo # also defines a singleton method
          end
          end
          end


          On another note, I feel like RepoType should either be:




          • completely removed and its responsibilities handled by Repo


          Or




          • named Host and be more cohesive by owning variables and methods REPO_TYPE and find_repo_type within it, along with the regex definitions associated with each Host


          Here's an example combining what I've outlined above:



          class Host
          class << self
          def all
          @all ||= {} # the `Host` singleton is an instance of `Class`, and can have @vars!
          end

          def ssh_regex(hostname)
          /blahblah#{hostname}blahblah/
          end
          end

          def initialize(name, hostname)
          @name = name
          @host_uri = URI("http://www.#{hostname}")
          end
          end
          ```





          share|improve this answer









          $endgroup$













            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212632%2fdownload-a-git-repo-without-git-folder%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0












            $begingroup$

            Ruby does not have an import system like python, so variables and methods at the top level like REPO_TYPES and temp_dir are effectively global variables and methods.



            Use modules to aggressively namespace, even for your main, especially when a small script begins to span more than one file:



            module Degit
            def self.main # define singleton method
            end
            end

            Degit.main # call singleton method


            This is also true for methods as well. def self.main in the example defines a singleton method on Degit itself. (Degit is a singleton in the sense that it will be the only instance of Module named "Degit", and main is a method it will now have).



            Ruby classes operate in the same way:



            class Foo
            class << self # opens singleton context
            def foo # also defines a singleton method
            end
            end
            end


            On another note, I feel like RepoType should either be:




            • completely removed and its responsibilities handled by Repo


            Or




            • named Host and be more cohesive by owning variables and methods REPO_TYPE and find_repo_type within it, along with the regex definitions associated with each Host


            Here's an example combining what I've outlined above:



            class Host
            class << self
            def all
            @all ||= {} # the `Host` singleton is an instance of `Class`, and can have @vars!
            end

            def ssh_regex(hostname)
            /blahblah#{hostname}blahblah/
            end
            end

            def initialize(name, hostname)
            @name = name
            @host_uri = URI("http://www.#{hostname}")
            end
            end
            ```





            share|improve this answer









            $endgroup$


















              0












              $begingroup$

              Ruby does not have an import system like python, so variables and methods at the top level like REPO_TYPES and temp_dir are effectively global variables and methods.



              Use modules to aggressively namespace, even for your main, especially when a small script begins to span more than one file:



              module Degit
              def self.main # define singleton method
              end
              end

              Degit.main # call singleton method


              This is also true for methods as well. def self.main in the example defines a singleton method on Degit itself. (Degit is a singleton in the sense that it will be the only instance of Module named "Degit", and main is a method it will now have).



              Ruby classes operate in the same way:



              class Foo
              class << self # opens singleton context
              def foo # also defines a singleton method
              end
              end
              end


              On another note, I feel like RepoType should either be:




              • completely removed and its responsibilities handled by Repo


              Or




              • named Host and be more cohesive by owning variables and methods REPO_TYPE and find_repo_type within it, along with the regex definitions associated with each Host


              Here's an example combining what I've outlined above:



              class Host
              class << self
              def all
              @all ||= {} # the `Host` singleton is an instance of `Class`, and can have @vars!
              end

              def ssh_regex(hostname)
              /blahblah#{hostname}blahblah/
              end
              end

              def initialize(name, hostname)
              @name = name
              @host_uri = URI("http://www.#{hostname}")
              end
              end
              ```





              share|improve this answer









              $endgroup$
















                0












                0








                0





                $begingroup$

                Ruby does not have an import system like python, so variables and methods at the top level like REPO_TYPES and temp_dir are effectively global variables and methods.



                Use modules to aggressively namespace, even for your main, especially when a small script begins to span more than one file:



                module Degit
                def self.main # define singleton method
                end
                end

                Degit.main # call singleton method


                This is also true for methods as well. def self.main in the example defines a singleton method on Degit itself. (Degit is a singleton in the sense that it will be the only instance of Module named "Degit", and main is a method it will now have).



                Ruby classes operate in the same way:



                class Foo
                class << self # opens singleton context
                def foo # also defines a singleton method
                end
                end
                end


                On another note, I feel like RepoType should either be:




                • completely removed and its responsibilities handled by Repo


                Or




                • named Host and be more cohesive by owning variables and methods REPO_TYPE and find_repo_type within it, along with the regex definitions associated with each Host


                Here's an example combining what I've outlined above:



                class Host
                class << self
                def all
                @all ||= {} # the `Host` singleton is an instance of `Class`, and can have @vars!
                end

                def ssh_regex(hostname)
                /blahblah#{hostname}blahblah/
                end
                end

                def initialize(name, hostname)
                @name = name
                @host_uri = URI("http://www.#{hostname}")
                end
                end
                ```





                share|improve this answer









                $endgroup$



                Ruby does not have an import system like python, so variables and methods at the top level like REPO_TYPES and temp_dir are effectively global variables and methods.



                Use modules to aggressively namespace, even for your main, especially when a small script begins to span more than one file:



                module Degit
                def self.main # define singleton method
                end
                end

                Degit.main # call singleton method


                This is also true for methods as well. def self.main in the example defines a singleton method on Degit itself. (Degit is a singleton in the sense that it will be the only instance of Module named "Degit", and main is a method it will now have).



                Ruby classes operate in the same way:



                class Foo
                class << self # opens singleton context
                def foo # also defines a singleton method
                end
                end
                end


                On another note, I feel like RepoType should either be:




                • completely removed and its responsibilities handled by Repo


                Or




                • named Host and be more cohesive by owning variables and methods REPO_TYPE and find_repo_type within it, along with the regex definitions associated with each Host


                Here's an example combining what I've outlined above:



                class Host
                class << self
                def all
                @all ||= {} # the `Host` singleton is an instance of `Class`, and can have @vars!
                end

                def ssh_regex(hostname)
                /blahblah#{hostname}blahblah/
                end
                end

                def initialize(name, hostname)
                @name = name
                @host_uri = URI("http://www.#{hostname}")
                end
                end
                ```






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 12 mins ago









                KacheKache

                20615




                20615






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212632%2fdownload-a-git-repo-without-git-folder%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

                    How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

                    Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...