#!/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. $:.unshift File.dirname(__FILE__) load 'git-mirror-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(self.info["web_url"]+"/mirror")) req.add_field("PRIVATE-TOKEN", @gl.config['apikey']) res = @gl.connection(req.uri).request(req) if res.code != "200" throw res end doc = Nokogiri::HTML(res.body) @cache[:session] = res["set-cookie"] @cache[:mirror_transaction] = 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) libremessages('msg2', 'Setting mirror URL for %s', @project_id) 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[:session]) # session id req.form_data = { "utf8" => "✓", "authenticity_token" => @cache[:mirror_transaction], # session state "project[mirror]" => (url.nil? ? "0" : "1"), "project[import_url]" => url.to_s, "project[mirror_user_id]" => @gl.user_id, } res = @gl.connection(req.uri).request(req) if res.code != "302" throw res end @cache.delete(:mirror) @cache.delete(:mirror_transaction) @cache.delete(:session) 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 return nil end def repo_mode return (self.mirrorURL.nil? ? "passive" : "active") end end def user_id req = Net::HTTP::Get.new(self.config['apiurl'] + 'user') req.add_field("PRIVATE-TOKEN", self.config['apikey']) res = self.connection(req.uri).request(req) if res.code != "200" raise Error.new([res, res.body]) end user = JSON::parse(res.body) return user["id"] end end if __FILE__ == $0 if ARGV.length != 1 throw "Usage: $0 ACCOUNT_NAME" end GitLabEE.new().repl(ARGV[1]) end