summaryrefslogtreecommitdiff
path: root/git-mirror-gitlab-ee
blob: 2ec79f333026d37bb3f0238d86605804e56f2e6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/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)
			if self.mirrorURL == URI(url)
				libremessages('msg2', 'Mirror URL ok')
				return self.mirrorURL
			end
			libremessages('msg2', 'Setting mirror URL')

			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