From 381a9b85d4ea82c5792fcc75383b2d0d98b72100 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Thu, 15 Dec 2016 04:36:19 -0500 Subject: more --- backends/github | 1 - backends/gitlab-ce | 205 ------------------------------------------- backends/gitlab-ee | 105 ---------------------- backends/gitmirrorbackend.rb | 30 ------- git-mirror-backend.rb | 30 +++++++ git-mirror-cgit | 69 +++++++++++++++ git-mirror-gitlab-ce | 202 ++++++++++++++++++++++++++++++++++++++++++ git-mirror-gitlab-ee | 105 ++++++++++++++++++++++ 8 files changed, 406 insertions(+), 341 deletions(-) delete mode 100755 backends/github delete mode 100755 backends/gitlab-ce delete mode 100755 backends/gitlab-ee delete mode 100644 backends/gitmirrorbackend.rb create mode 100644 git-mirror-backend.rb create mode 100755 git-mirror-cgit create mode 100755 git-mirror-gitlab-ce create mode 100755 git-mirror-gitlab-ee diff --git a/backends/github b/backends/github deleted file mode 100755 index 49ffd26..0000000 --- a/backends/github +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/env ruby diff --git a/backends/gitlab-ce b/backends/gitlab-ce deleted file mode 100755 index 84cb933..0000000 --- a/backends/gitlab-ce +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env ruby -# coding: utf-8 - -# http://docs.gitlab.com/ee/workflow/repository_mirroring.html -# https://gitlab.com/gitlab-org/gitlab-ee/issues/767 - -load 'gitmirrorbackend.rb' -require 'net/http' -require 'uri' -require 'cgi' -require 'json' - -class GitLabCE < GitMirrorBackend - class Error < RuntimeError - def initialize(obj) - @obj = obj - end - def obj - return @obj - end - end - - def initialize() - @connections = {} - @projects = {} - @config = {} - end - - def connection(uri) - key=URI(uri.scheme+":") - key.host = uri.host - key.port = uri.port - - @connections[key] ||= Net::HTTP::start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') - return @connections[key] - end - - def finish - @connections.each do |k,v| - v.finish() - end - @connections = {} - super - end - - def config - return @config - end - - # Project - def project(project_id) - unless @projects.has_key?(project_id) - @projects[project_id] = Project.new(self, project_id) - end - return @projects[project_id] - end - class Project - def initialize(gl, project_id) - @gl = gl - @project_id = project_id - @cache = {} - end - - def info - unless @cache.has_key?(:info) - req = Net::HTTP::Get.new(@gl.config['apiurl'] + "projects/" + CGI::escape(@project_id)) - req.add_field("PRIVATE-TOKEN", @gl.config['apikey']) - res = @gl.connection(req.uri).request(req) - case res.code - when "200" - @cache[:info] = JSON::parse(res.body) - when "404" - @cache[:info] = nil - else - raise Error.new(res) - end - end - return @cache[:info] - end - - def info=(i) - @cache[:info] = i - end - - def get_meta - return self.info.select{|k,v| @vars.include?(k.to_sym)} - end - - def set_meta(map) - mirror = map["mirror"] - map.delete("mirror") - - illegal = map.select{|k,v| not @vars.include?(k.to_sym)} - if illegal.count > 0 - raise Error.new(illegal) - end - - if info == nil - # create - req = Net::HTTP::Put.new(@gl.config['apiurl'] + "projects") - req.add_field("PRIVATE-TOKEN", @gl.config['apikey']) - req.add_field("Content-Type", "application/json") - map["path"] = @project_id - if not mirror.nil? - map["import_url"] = mirror - end - req.body = JSON::dump(map) - res = @gl.connection(req.uri).request(req) - if res.code != "201" - raise Error.new(res) - end - info = JSON::parse(res.body) - else - # update - req = Net::HTTP::Put.new(@gl.config['apiurl'] + "projects/" + CGI::escape(info["id"].to_s)) - req.add_field("PRIVATE-TOKEN", @gl.config['apikey']) - req.add_field("Content-Type", "application/json") - req.body = JSON::dump(map) - res = @gl.connection(req.uri).request(req) - if res.code != "200" - raise Error.new(res) - end - info = JSON::parse(res.body) - end - return self.get_meta - end - - def repo_mode - return "passive" - end - end - - # commands - def cmd_config(*args) - args.each do |arg| - key, val = arg.split('=', 2) - case key - when "apiurl" - val = URI(val) - unless val.path.end_with?("/") - val.path += "/" - end - end - @config[key] = val - end - end - - def cmd_get_meta(project_id) - return self.project(project_id).get_meta() - end - - def cmd_set_meta(project_id, *pairs) - map = {} - pairs.each do |pair| - key, val = arg.split('=', 2) - map[key] = val - end - return self.project(project_id).set_meta(map) - end - - def cmd_push_url(project_id) - return self.project(project_id).info["ssh_url_to_repo"] - end - - def cmd_pull_url(project_id) - return self.project(project_id).info["http_url_to_repo"] - end - - def cmd_repo_mode(project_id) - return self.project(projecT_id).repo_mode() - end - - def vars - # API docs suck, look at `lib/api/projects.rb` instead. - return [ - :builds_enabled, # create | create-user | edit - :container_registry_enabled, # XXX # create | | edit - :default_branch, # | create-user | edit - :description, # create | create-user | edit - #:import_url, # XXX # create | create-user | - :issues_enabled, # create | create-user | edit - :lfs_enabled, # create | create-user | edit - :merge_requests_enabled, # create | create-user | edit - :name, # create | create-user | edit - #:namespace_id, # create | | - :only_allow_merge_if_build_succeeds, # create | create-user | edit - #:path, # create | | edit - :public, # create | create-user | edit - :public_builds, # create | create-user | edit - :request_access_enabled, # create | create-user | edit - :shared_runners_enabled, # create | create-user | edit - :snippets_enabled, # create | create-user | edit - :visibility_level, # create | create-user | edit - :wiki_enabled, # create | create-user | edit - :only_allow_merge_if_all_discussions_are_resolved # create | create-user | edit - ] - end -end - -if __FILE__ == $0 - if ARGV.length != 1 - raise "Usage: $0 ACCOUNT_NAME" - end - GitLabCE.new().repl(ARGV[1]) -end diff --git a/backends/gitlab-ee b/backends/gitlab-ee deleted file mode 100755 index 5467e16..0000000 --- a/backends/gitlab-ee +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env ruby -# coding: utf-8 - -# GitLab EE supports configuring a "project" (GitLab's term for a -# repository+metadata) to display as a mirror of another repository. -# -# http://docs.gitlab.com/ee/workflow/repository_mirroring.html -# -# Unfortunately, the JSON API doesn't support this -# -# https://gitlab.com/gitlab-org/gitlab-ee/issues/767 -# -# So, we must use the (undocumented!) HTTP API, which is actually -# pretty clean, except that screen-scraping the reads (via nokogiri) -# is gross, and that the error messages are unhelpful. - -load 'gitlab-ce' -require 'net/http' -require 'uri' -require 'nokogiri' - -class GitLabEE < GitLabCE - def project(project_id) - unless @projects.has_key?(project_id) - @projects[project_id] = Project.new(self, project_id) - end - return @projects[project_id] - end - class Project < GitLabCE::Project - def mirrorURL - unless @cache.has_key?(:mirror) - req = Net::HTTP::Get.new(URI(_info["web_url"]+"/mirror")) - req.add_field("PRIVATE-TOKEN", @api_key) - res = @gl.connection(req.uri).request(req) - if res.code != "200" - throw res - end - @cache[:mirror_res]=res - doc = Nokogiri::HTML(res.body) - - @cache[:mirror_cookie] = res["set-cookie"] - @cache[:mirror_token] = doc.css('input[name="authenticity_token"]').first["value"] - is_mirror = doc.css("#project_mirror").first["checked"] - if !is_mirror - @cache[:mirror] = nil - else - @cache[:mirror] = URI(doc.css("#project_import_url").first["value"]) - end - end - return @cache[:mirror] - end - - def mirrorURL=(url) - self.mirrorURL() - - req = Net::HTTP::Patch.new(URI(self.info["web_url"]+"/mirror")) - req.add_field("PRIVATE-TOKEN", @gl.config['apikey']) # authenticate - req.add_field("Cookie", @cache[:mirror_cookie]) # session id - req.form_data = { - "utf8" => "✓", - "authenticity_token" => @cache[:mirror_token], # session state - "project[mirror]" => (url.nil? ? "0" : "1"), - "project[import_url]" => url.to_s, - } - - res = @gl.connection(req.uri).request(req) - if res.code != "302" - throw res - end - - @cache.delete(:mirror) - @cache.delete(:mirror_token) - @cache.delete(:mirror_cookie) - return URI(url) - end - - def get_meta - map = super - map["mirror"] = self.mirrorURL.to_s - return map - end - - def set_meta(map) - if map.has_key?("mirror") - url = map["mirror"] - map.delete("mirror") - super(map) - self.mirrorURL = url - else - super(map) - end - end - - def repo_mode - return (self.mirrorURL.nil? ? "active" : "passive") - end - end -end - -if __FILE__ == $0 - if ARGV.length != 1 - throw "Usage: $0 ACCOUNT_NAME" - end - GitLabEE.new().repl(ARGV[1]) -end diff --git a/backends/gitmirrorbackend.rb b/backends/gitmirrorbackend.rb deleted file mode 100644 index 25f7c3e..0000000 --- a/backends/gitmirrorbackend.rb +++ /dev/null @@ -1,30 +0,0 @@ -# coding: utf-8 - -require 'shellwords' - -class GitMirrorBackend - def repl(accountName) - @accountName = account_name - $stdin.each do |line| - args = line.shellsplit - out = self.send('cmd_'+args[0].gsub('-', '_'), *args[1..0]) - if out.is_a? String - puts out - elsif out.is_a? Array - out.each do |outline| - puts outline - end - elsif out.is_a? Hash - out.each do |k,v| - puts "#{k}=#{v}" - end - else - raise "I don't know what to do with output value: #{out}" - end - end - self.finish - end - def finish - # noop - end -end diff --git a/git-mirror-backend.rb b/git-mirror-backend.rb new file mode 100644 index 0000000..25f7c3e --- /dev/null +++ b/git-mirror-backend.rb @@ -0,0 +1,30 @@ +# coding: utf-8 + +require 'shellwords' + +class GitMirrorBackend + def repl(accountName) + @accountName = account_name + $stdin.each do |line| + args = line.shellsplit + out = self.send('cmd_'+args[0].gsub('-', '_'), *args[1..0]) + if out.is_a? String + puts out + elsif out.is_a? Array + out.each do |outline| + puts outline + end + elsif out.is_a? Hash + out.each do |k,v| + puts "#{k}=#{v}" + end + else + raise "I don't know what to do with output value: #{out}" + end + end + self.finish + end + def finish + # noop + end +end diff --git a/git-mirror-cgit b/git-mirror-cgit new file mode 100755 index 0000000..8c1b799 --- /dev/null +++ b/git-mirror-cgit @@ -0,0 +1,69 @@ +#!/usr/bin/env ruby +# coding: utf-8 + +load 'git-mirror-backend.rb' +require 'uri' +require 'open-uri' +require 'nokogiri' + +class Cgit < GitMirrorBackend + def initialize() + @config = {} + end + + def cmd_config(*args) + args.each do |arg| + key, val = arg.split('=', 2) + case key + when "url" + val = URI(val) + unless val.path.end_with?("/") + val.path += "/" + end + end + @config[key] = val + end + end + + def cmd_get_meta(path) + doc = Nokogiri::HTML(open(@config[url]+path)) + head = open(@config[url]+path+'/HEAD').read + return { + 'description' => doc.css('#header .sub')[0].text, + 'owner' => doc.css('#header .sub')[1].text, + 'default_branch' => head.split("\n")[0].sub(/^ref: /, '').sub(/^refs\/heads\//, ''), + } + end + + def cmd_set_meta(path, *pairs) + raise "no can do" + end + + def urls(path) + doc = Nokogiri::HTML(open(@config[url]+path)) + return doc.css('a[rel="vcs-git"]').map{|a| a['href']} + end + + def cmd_push_url(path) + prefs = ['ssh', 'https', 'http', 'git'] + return self.urls.sort_by{|url| prefs.index(url)} + end + + def cmd_pull_url(path) + # prefer https ahead of git because of a bug in git-daemon + # with '~'. + prefs = ['https', 'git', 'http', 'ssh'] + return self.urls.sort_by{|url| prefs.index(url)} + end + + def cmd_repo_mode(path) + return "passive" + end +end + +if __FILE__ == $0 + if ARGV.length != 1 + throw "Usage: $0 ACCOUNT_NAME" + end + Cgit.new().repl(ARGV[1]) +end diff --git a/git-mirror-gitlab-ce b/git-mirror-gitlab-ce new file mode 100755 index 0000000..a8d885e --- /dev/null +++ b/git-mirror-gitlab-ce @@ -0,0 +1,202 @@ +#!/usr/bin/env ruby +# coding: utf-8 + +load 'git-mirror-backend.rb' +require 'net/http' +require 'uri' +require 'cgi' +require 'json' + +class GitLabCE < GitMirrorBackend + class Error < RuntimeError + def initialize(obj) + @obj = obj + end + def obj + return @obj + end + end + + def initialize() + @connections = {} + @projects = {} + @config = {} + end + + def connection(uri) + key=URI(uri.scheme+":") + key.host = uri.host + key.port = uri.port + + @connections[key] ||= Net::HTTP::start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') + return @connections[key] + end + + def finish + @connections.each do |k,v| + v.finish() + end + @connections = {} + super + end + + def config + return @config + end + + # Project + def project(project_id) + unless @projects.has_key?(project_id) + @projects[project_id] = Project.new(self, project_id) + end + return @projects[project_id] + end + class Project + def initialize(gl, project_id) + @gl = gl + @project_id = project_id + @cache = {} + end + + def info + unless @cache.has_key?(:info) + req = Net::HTTP::Get.new(@gl.config['apiurl'] + "projects/" + CGI::escape(@project_id)) + req.add_field("PRIVATE-TOKEN", @gl.config['apikey']) + res = @gl.connection(req.uri).request(req) + case res.code + when "200" + @cache[:info] = JSON::parse(res.body) + when "404" + @cache[:info] = nil + else + raise Error.new(res) + end + end + return @cache[:info] + end + + def info=(i) + @cache[:info] = i + end + + def get_meta + return self.info.select{|k,v| @vars.include?(k.to_sym)} + end + + def set_meta(map) + mirror = map["mirror"] + map.delete("mirror") + + illegal = map.select{|k,v| not @vars.include?(k.to_sym)} + if illegal.count > 0 + raise Error.new(illegal) + end + + if info == nil + # create + req = Net::HTTP::Put.new(@gl.config['apiurl'] + "projects") + req.add_field("PRIVATE-TOKEN", @gl.config['apikey']) + req.add_field("Content-Type", "application/json") + map["path"] = @project_id + if not mirror.nil? + map["import_url"] = mirror + end + req.body = JSON::dump(map) + res = @gl.connection(req.uri).request(req) + if res.code != "201" + raise Error.new(res) + end + info = JSON::parse(res.body) + else + # update + req = Net::HTTP::Put.new(@gl.config['apiurl'] + "projects/" + CGI::escape(info["id"].to_s)) + req.add_field("PRIVATE-TOKEN", @gl.config['apikey']) + req.add_field("Content-Type", "application/json") + req.body = JSON::dump(map) + res = @gl.connection(req.uri).request(req) + if res.code != "200" + raise Error.new(res) + end + info = JSON::parse(res.body) + end + return self.get_meta + end + + def repo_mode + return "passive" + end + end + + # commands + def cmd_config(*args) + args.each do |arg| + key, val = arg.split('=', 2) + case key + when "apiurl" + val = URI(val) + unless val.path.end_with?("/") + val.path += "/" + end + end + @config[key] = val + end + end + + def cmd_get_meta(project_id) + return self.project(project_id).get_meta() + end + + def cmd_set_meta(project_id, *pairs) + map = {} + pairs.each do |pair| + key, val = arg.split('=', 2) + map[key] = val + end + return self.project(project_id).set_meta(map) + end + + def cmd_push_url(project_id) + return self.project(project_id).info["ssh_url_to_repo"] + end + + def cmd_pull_url(project_id) + return self.project(project_id).info["http_url_to_repo"] + end + + def cmd_repo_mode(project_id) + return self.project(projecT_id).repo_mode() + end + + def vars + # API docs suck, look at `lib/api/projects.rb` instead. + return [ + :builds_enabled, # create | create-user | edit + :container_registry_enabled, # XXX # create | | edit + :default_branch, # | create-user | edit + :description, # create | create-user | edit + #:import_url, # XXX # create | create-user | + :issues_enabled, # create | create-user | edit + :lfs_enabled, # create | create-user | edit + :merge_requests_enabled, # create | create-user | edit + :name, # create | create-user | edit + #:namespace_id, # create | | + :only_allow_merge_if_build_succeeds, # create | create-user | edit + #:path, # create | | edit + :public, # create | create-user | edit + :public_builds, # create | create-user | edit + :request_access_enabled, # create | create-user | edit + :shared_runners_enabled, # create | create-user | edit + :snippets_enabled, # create | create-user | edit + :visibility_level, # create | create-user | edit + :wiki_enabled, # create | create-user | edit + :only_allow_merge_if_all_discussions_are_resolved # create | create-user | edit + ] + end +end + +if __FILE__ == $0 + if ARGV.length != 1 + raise "Usage: $0 ACCOUNT_NAME" + end + GitLabCE.new().repl(ARGV[1]) +end diff --git a/git-mirror-gitlab-ee b/git-mirror-gitlab-ee new file mode 100755 index 0000000..5467e16 --- /dev/null +++ b/git-mirror-gitlab-ee @@ -0,0 +1,105 @@ +#!/usr/bin/env ruby +# coding: utf-8 + +# GitLab EE supports configuring a "project" (GitLab's term for a +# repository+metadata) to display as a mirror of another repository. +# +# http://docs.gitlab.com/ee/workflow/repository_mirroring.html +# +# Unfortunately, the JSON API doesn't support this +# +# https://gitlab.com/gitlab-org/gitlab-ee/issues/767 +# +# So, we must use the (undocumented!) HTTP API, which is actually +# pretty clean, except that screen-scraping the reads (via nokogiri) +# is gross, and that the error messages are unhelpful. + +load 'gitlab-ce' +require 'net/http' +require 'uri' +require 'nokogiri' + +class GitLabEE < GitLabCE + def project(project_id) + unless @projects.has_key?(project_id) + @projects[project_id] = Project.new(self, project_id) + end + return @projects[project_id] + end + class Project < GitLabCE::Project + def mirrorURL + unless @cache.has_key?(:mirror) + req = Net::HTTP::Get.new(URI(_info["web_url"]+"/mirror")) + req.add_field("PRIVATE-TOKEN", @api_key) + res = @gl.connection(req.uri).request(req) + if res.code != "200" + throw res + end + @cache[:mirror_res]=res + doc = Nokogiri::HTML(res.body) + + @cache[:mirror_cookie] = res["set-cookie"] + @cache[:mirror_token] = doc.css('input[name="authenticity_token"]').first["value"] + is_mirror = doc.css("#project_mirror").first["checked"] + if !is_mirror + @cache[:mirror] = nil + else + @cache[:mirror] = URI(doc.css("#project_import_url").first["value"]) + end + end + return @cache[:mirror] + end + + def mirrorURL=(url) + self.mirrorURL() + + req = Net::HTTP::Patch.new(URI(self.info["web_url"]+"/mirror")) + req.add_field("PRIVATE-TOKEN", @gl.config['apikey']) # authenticate + req.add_field("Cookie", @cache[:mirror_cookie]) # session id + req.form_data = { + "utf8" => "✓", + "authenticity_token" => @cache[:mirror_token], # session state + "project[mirror]" => (url.nil? ? "0" : "1"), + "project[import_url]" => url.to_s, + } + + res = @gl.connection(req.uri).request(req) + if res.code != "302" + throw res + end + + @cache.delete(:mirror) + @cache.delete(:mirror_token) + @cache.delete(:mirror_cookie) + return URI(url) + end + + def get_meta + map = super + map["mirror"] = self.mirrorURL.to_s + return map + end + + def set_meta(map) + if map.has_key?("mirror") + url = map["mirror"] + map.delete("mirror") + super(map) + self.mirrorURL = url + else + super(map) + end + end + + def repo_mode + return (self.mirrorURL.nil? ? "active" : "passive") + end + end +end + +if __FILE__ == $0 + if ARGV.length != 1 + throw "Usage: $0 ACCOUNT_NAME" + end + GitLabEE.new().repl(ARGV[1]) +end -- cgit v1.2.3-54-g00ecf