#!/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