#!/usr/bin/env ruby require "optparse" require "open3" ROOT = File.expand_path("../../", __FILE__) # Break a repository URL into its separate components def parse_url(input) hosts = "github\.com|bitbucket\.org|gitlab\.com" # HTTPS/HTTP link pointing to recognised hosts if input =~ /^(?:https?:\/\/)?(?:[^.@]+@)?(?:www\.)?(#{hosts})\/([^\/]+)\/([^\/]+)/i { host: $1.downcase(), user: $2, repo: $3.sub(/\.git$/, "") } # SSH elsif input =~ /^git@(#{hosts}):([^\/]+)\/([^\/]+)\.git$/i { host: $1.downcase(), user: $2, repo: $3 } # provider:user/repo elsif input =~ /^(github|bitbucket|gitlab):\/?([^\/]+)\/([^\/]+)\/?$/i { host: $1.downcase(), user: $2, repo: $3 } # user/repo - Common GitHub shorthand elsif input =~ /^\/?([^\/]+)\/([^\/]+)\/?$/ { host: "github.com", user: $1, repo: $2 } else raise "Unsupported URL: #{input}" end end # Isolate the vendor-name component of a submodule path def parse_submodule(name) name =~ /^(?:.*(?:vendor\/)?grammars\/)?([^\/]+)/i path = "vendor/grammars/#{$1}" unless File.exist?("#{ROOT}/" + path) warn "Submodule '#{path}' does not exist. Aborting." exit 1 end path end # Print debugging feedback to STDOUT if running with --verbose def log(msg) puts msg if $verbose end $aborted = false def command(*args) log "$ #{args.join(' ')}" output, status = Open3.capture2e(*args) if !status.success? output = output.each_line { |line| " > #{line}" } unless $aborted $aborted = true warn "Command failed. Aborting." raise output else warn output exit 1 end end end usage = <<~EOH Usage: #{$0} [-v|--verbose] [--replace grammar] url Examples: #{$0} https://github.com/Alhadis/language-roff #{$0} --replace sublime-apl https://github.com/Alhadis/language-apl EOH $replace = nil $verbose = true OptionParser.new do |opts| opts.banner = usage opts.on("-q", "--quiet", "Do not print output unless there's a failure") do $verbose = false end opts.on("-rSUBMODULE", "--replace=SUBMODDULE", "Replace an existing grammar submodule.") do |name| $replace = name end end.parse! $url = ARGV[0] # No URL? Print a usage message and bail. unless $url warn usage exit 1 end # Flags to track which changes should be reverted on an error $gitmodules = File.read("#{ROOT}/.gitmodules") $git_config = File.read("#{ROOT}/.git/config") $vendor_list = File.read("#{ROOT}/vendor/README.md") def restore_configs File.write("#{ROOT}/.gitmodules", $gitmodules) File.write("#{ROOT}/.git/config", $git_config) end begin # Exit early if Docker isn't installed or running. log "Checking Docker is installed and running" command('docker', 'ps') # Ensure the given URL is an HTTPS link parts = parse_url $url $https = "https://#{parts[:host]}/#{parts[:user]}/#{parts[:repo]}" $repo_new = "vendor/grammars/#{parts[:repo]}" $repo_old = parse_submodule($replace) if $replace Dir.chdir(ROOT) if $repo_old log "Deregistering: #{$repo_old}" command('git', 'submodule', 'deinit', '-f', $repo_old) command('git', 'rm', '-rf', $repo_old) command('script/grammar-compiler', 'update', '-f') end log "Registering new submodule: #{$repo_new}" command('git', 'submodule', 'add', '-f', $https, $repo_new) command('script/grammar-compiler', 'add', $repo_new) log "Confirming license" if $repo_old command('script/licensed') else repo_abs = File.absolute_path($repo_new) command('script/licensed', '--module', repo_abs) end log "Updating grammar documentation in vendor/README.md" command('bundle', 'exec', 'rake', 'samples') command('script/sort-submodules') command('script/list-grammars') rescue => ex log ex if $repo_new `git reset HEAD .gitmodules #{$repo_new}` `git checkout -- vendor/licenses` `rm -rf #{$repo_new}` `rm -rf .git/modules/#{$repo_new}/` restore_configs() end if $repo_old `rm -rf #{$repo_old}` `git submodule add -f "#{$https}", "#{$repo_old}"` restore_configs() end File.write("#{ROOT}/vendor/README.md", $vendor_list) `git reset HEAD vendor/licenses` `git checkout -- vendor/licenses` exit 1 end