api_deploy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/bitbucket.rb ADDED
@@ -0,0 +1,68 @@
1
+ class Bitbucket
2
+ include API
3
+ LIMIT=1000.to_s
4
+
5
+ HOOK_EXCLUSIONS = ['WSC','CM','YOMS']
6
+
7
+ def initialize
8
+ create_api( ConfigStore.bitbucket )
9
+ end
10
+
11
+ def projects
12
+ @projects ||= Project.get_all(self)
13
+ end
14
+
15
+ def set_master_branch_protected_on_all_projects
16
+ projects.map do |project|
17
+ project.repositories.map do |r|
18
+ r.set_master_branch_protected unless r.master_branch_protected?
19
+ end
20
+ end
21
+ end
22
+
23
+ def set_hook_status_on_all_projects(key,status,settings=nil)
24
+ projects.map do |project|
25
+ next if HOOK_EXCLUSIONS.include?(project.key)
26
+ project.set_hook_status(key, status, settings)
27
+ end
28
+ end
29
+
30
+ def remove_hooks_on_exclusions
31
+ projects.select {|p| HOOK_EXCLUSIONS.include? p.key }.each do |project|
32
+ [
33
+ 'org.christiangalsterer.stash-filehooks-plugin:filesize-hook',
34
+ "org.christiangalsterer.stash-filehooks-plugin:filename-hook",
35
+ ].each do |key|
36
+ project.set_hook_status(key, false)
37
+ end
38
+ end
39
+ end
40
+
41
+ def apply_restrictions
42
+ set_hook_status_on_all_projects(
43
+ "org.christiangalsterer.stash-filehooks-plugin:filesize-hook",
44
+ true,
45
+ {
46
+ "pattern-1"=>".*",
47
+ "size-1"=>"10485760",
48
+ "pattern-exclude-1"=>"",
49
+ "pattern-branches-1"=>""
50
+ }.to_json
51
+ )
52
+
53
+ set_hook_status_on_all_projects(
54
+ "org.christiangalsterer.stash-filehooks-plugin:filename-hook",
55
+ false,
56
+ {
57
+ "pattern"=>"\\.(h26.?|mp.?.?|avi|webm|flv|tar(\\..?.?.?)?|zip|7z|rar|exe|msi|rpm|deb)$",
58
+ "pattern-exclude"=>"",
59
+ "pattern-branches"=>""
60
+ }.to_json
61
+ )
62
+
63
+ set_master_branch_protected_on_all_projects
64
+
65
+ # cleanup TRAIN project
66
+ projects.detect {|p| p.key == 'TRAIN'}.move_all_repos_to_project('TOLD')
67
+ end
68
+ end
@@ -0,0 +1,34 @@
1
+ class Project
2
+ include API
3
+ LIMIT=1000.to_s
4
+
5
+ def self.get_all(server)
6
+ data = server.request(:get, "rest/api/1.0/projects?limit=#{LIMIT}")
7
+ data['values'].map {|p| new(server,p) }
8
+ end
9
+
10
+ attr_reader :name, :key
11
+
12
+ def initialize(server,data)
13
+ @bb = server
14
+ @name = data['name']
15
+ @key = data['key']
16
+ end
17
+
18
+ def repositories
19
+ @repositories ||= Repository.get_all(@bb,key)
20
+ end
21
+
22
+ def move_all_repos_to_project(new_project)
23
+ repositories.each do |r|
24
+ r.move_repo_to_project(new_project)
25
+ end
26
+ end
27
+
28
+ def set_hook_status(key,status,settings=nil)
29
+ repositories.each do |r|
30
+ r.set_hook_settings(key, settings) if status && settings
31
+ r.set_hook_status(key, status)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,66 @@
1
+ class Repository
2
+ include API
3
+ LIMIT=1000.to_s
4
+
5
+ def self.get_all(bb,project)
6
+ data = bb.request(:get, "rest/api/1.0/projects/#{project}/repos/?limit=#{LIMIT}")
7
+ data['values'].map {|p| new(bb,p) }
8
+ end
9
+
10
+ attr_reader :bb, :name, :project
11
+
12
+ def initialize(server,data)
13
+ @bb = server
14
+ @name = data['slug']
15
+ @project = data['project']['key']
16
+ end
17
+
18
+ def branch_permissions
19
+ bb.request(:get, "rest/branch-permissions/2.0/projects/#{project}/repos/#{name}/restrictions")['values']
20
+ end
21
+
22
+ def master_branch_protected?
23
+ !! branch_permissions.detect do |c|
24
+ c["type"] == 'fast-forward-only' && c["matcher"]["id"] == 'refs/heads/master'
25
+ end
26
+ end
27
+
28
+ def set_master_branch_protected
29
+ perm = {
30
+ "type"=>"fast-forward-only",
31
+ "matcher"=>
32
+ {"id"=>"refs/heads/master",
33
+ "displayId"=>"master",
34
+ "type"=>{"id"=>"BRANCH", "name"=>"Branch"},
35
+ "active"=>true},
36
+ "users"=>[],
37
+ "groups"=>[]
38
+ }.to_json
39
+ bb.request(:post, "rest/branch-permissions/2.0/projects/#{project}/repos/#{name}/restrictions", perm)
40
+ end
41
+
42
+ def move_repo_to_project(new_project)
43
+ bb.request(:post, "/rest/api/1.0/projects/#{project}/repos/#{name}",{project: {key: new_project}}.to_json)
44
+ bb.request(:delete, "/rest/api/1.0/projects/#{project}/repos/#{name}")
45
+ end
46
+
47
+ def hooks
48
+ bb.request(:get, "rest/api/1.0/projects/#{project}/repos/#{name}/settings/hooks")
49
+ end
50
+
51
+ def set_hook_status(key, status)
52
+ if status
53
+ bb.request(:put, "rest/api/1.0/projects/#{project}/repos/#{name}/settings/hooks/#{key}/enabled")
54
+ else
55
+ bb.request(:delete, "rest/api/1.0/projects/#{project}/repos/#{name}/settings/hooks/#{key}/enabled")
56
+ end
57
+ end
58
+
59
+ def hook_settings(key)
60
+ bb.request(:get, "rest/api/1.0/projects/#{project}/repos/#{name}/settings/hooks/#{key}/settings")
61
+ end
62
+
63
+ def set_hook_settings(key, settings)
64
+ bb.request(:put, "rest/api/1.0/projects/#{project}/repos/#{name}/settings/hooks/#{key}/settings", settings)
65
+ end
66
+ end
@@ -0,0 +1,40 @@
1
+ class ConfigStore
2
+ @@defaults_path = File.join(File.dirname(__FILE__),'../config/defaults.json')
3
+ @@overrides_path = File.expand_path('~/.config/api_deploy_overrides.json')
4
+
5
+ def self.defaults_path
6
+ @@defaults_path
7
+ end
8
+
9
+ def self.defaults_path=(path)
10
+ @@defaults_path = path
11
+ end
12
+
13
+ def self.overrides_path
14
+ @@overrides_path
15
+ end
16
+
17
+ def self.overrides_path=(path)
18
+ @@overrides_path = path
19
+ end
20
+
21
+ def self.load_config
22
+ warn "ConfigStoreuration overrides file not present at #{overrides_path}" unless File.exists?(overrides_path)
23
+ defaults = Hashie::Mash.new(JSON.parse(File.read(defaults_path))) if File.exists?(defaults_path)
24
+ overrides = JSON.parse(File.read(overrides_path)) if File.exists?(overrides_path)
25
+ defaults.deep_merge(overrides || {})
26
+ end
27
+
28
+ class << self
29
+ def teamcity; @@ConfigStore.teamcity; end
30
+ def artifactory; @@ConfigStore.artifactory; end
31
+ def octopus; @@ConfigStore.octopus; end
32
+ def bitbucket; @@ConfigStore.bitbucket; end
33
+ end
34
+
35
+ def self.set_config
36
+ @@ConfigStore = self.load_config
37
+ end
38
+
39
+ self.set_config
40
+ end
data/lib/github.rb ADDED
@@ -0,0 +1,13 @@
1
+ class GithubApi
2
+ attr_reader :api
3
+
4
+ ORG = 'YTech'
5
+
6
+
7
+ def initialize
8
+ @api = Github.new(
9
+ basic_auth: "#{ENV['GITHUB_API_USER']}:#{ENV['GITHUB_API_PASS']}",
10
+ org: ORG
11
+ )
12
+ end
13
+ end
data/lib/ldap.rb ADDED
@@ -0,0 +1,115 @@
1
+ class LdapApi
2
+
3
+ YOOX = {
4
+ name: 'YOOX',
5
+ host: 'ydcrootblq.yoox.net',
6
+ base: 'dc=yoox,dc=net',
7
+ port: 389,
8
+ user: ENV['YOOX_BIND_USER'],
9
+ pass: ENV['YOOX_BIND_PASS'],
10
+ }
11
+ NAP = {
12
+ name: 'LONDON',
13
+ host: 'RODC02-PR-IMO.london.net-a-porter.com',
14
+ base: 'dc=london,dc=net-a-porter,dc=com',
15
+ port: 389,
16
+ user: ENV['NAP_BIND_USER'],
17
+ pass: ENV['NAP_BIND_PASS'],
18
+ }
19
+
20
+ DOMAINS = [ YOOX, NAP ]
21
+
22
+ def domains
23
+ domain_structs = DOMAINS.map do |domain|
24
+ d = OpenStruct.new(
25
+ name: domain[:name],
26
+ user: domain[:user],
27
+ pass: domain[:pass],
28
+ ldap: Net::LDAP.new(
29
+ host: domain[:host],
30
+ port: domain[:port],
31
+ base: domain[:base],
32
+ auth: {
33
+ method: :simple,
34
+ username: domain[:user],
35
+ password: domain[:pass],
36
+ },
37
+ )
38
+ )
39
+ raise "BIND ERROR: #{domain}" unless d.ldap.bind
40
+ d
41
+ end
42
+ end
43
+
44
+ def auth?(username, password)
45
+ domains.each do |domain|
46
+ domain.ldap.authenticate domain.name + "\\" + username, password
47
+ return true if domain.ldap.bind
48
+ end
49
+ return false
50
+ end
51
+
52
+ def groups(name)
53
+ filter = Net::LDAP::Filter.eq("sAMAccountName", name)
54
+ results = []
55
+ domains.map do |domain|
56
+ domain.ldap.search(filter: filter) do |entry|
57
+ results << entry.memberof.map {|e| e.sub(/^CN=/,'').sub(/,.*$/,'') }
58
+ end
59
+ domain.ldap.get_operation_result
60
+ end
61
+ results.flatten
62
+ end
63
+
64
+ def user(name)
65
+ filter = Net::LDAP::Filter.eq("sAMAccountName", name)
66
+ results = []
67
+ domains.map do |domain|
68
+ domain.ldap.search(filter: filter) do |entry|
69
+ results << entry
70
+ end
71
+ domain.ldap.get_operation_result
72
+ end
73
+ results.flatten
74
+ end
75
+
76
+ def group(name)
77
+ filter = Net::LDAP::Filter.eq("cn", name)
78
+ results = []
79
+ domains.map do |domain|
80
+ domain.ldap.search(filter: filter) do |entry|
81
+ results << entry
82
+ end
83
+ domain.ldap.get_operation_result
84
+ end
85
+ results.flatten
86
+ end
87
+
88
+ def in_group?(name, group)
89
+ groups(name).include?(group)
90
+ end
91
+
92
+ def users_in_group(group)
93
+ filter = Net::LDAP::Filter.eq("cn", group)
94
+ results = []
95
+ domains.map do |domain|
96
+ domain.ldap.search(filter: filter) do |entry|
97
+ results << entry.member.map {|e| user_from_name(e.sub(/^CN=/,'').sub(/,.*$/,'')) }
98
+ end
99
+ domain.ldap.get_operation_result
100
+ end
101
+ results.flatten
102
+ end
103
+
104
+ def user_from_name(name)
105
+ filter = Net::LDAP::Filter.eq("cn", name)
106
+ results = []
107
+ domains.map do |domain|
108
+ domain.ldap.search(filter: filter) do |entry|
109
+ results << entry[:samaccountname]
110
+ end
111
+ domain.ldap.get_operation_result
112
+ end
113
+ results.flatten
114
+ end
115
+ end
data/lib/libraries.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'artifactory'
2
+ require 'faraday'
3
+ require 'json'
4
+ require 'hashie'
5
+ require 'logging'
6
+ require 'require_all'
7
+ require 'net/ldap'
8
+ require 'github_api'
9
+
10
+ require_rel '../lib'
data/lib/log.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Log
2
+ class << self
3
+ def logger
4
+ @logger ||= ::Logging.logger(STDOUT)
5
+ @logger.level = ENV.fetch("LOG_LEVEL", "warn").to_sym
6
+ @logger
7
+ end
8
+ def warn(m); logger.warn(m); end
9
+ def info(m); logger.info(m); end
10
+ def debug(m); logger.debug(m); end
11
+ def level=(l); logger.level = l; end
12
+ end
13
+ end
@@ -0,0 +1,56 @@
1
+ class OctopusApi
2
+ include API
3
+
4
+ RESOURCE_TYPES = ['Environments','Projects','ProjectGroups','NugetFeeds','LibraryVariableSets','Machines','Lifecycles','Users','Releases','Deployments']
5
+
6
+
7
+ def initialize
8
+ create_api(ConfigStore.octopus)
9
+ end
10
+
11
+ def create_resource(type, query)
12
+ check_type type
13
+ request(:post, "/#{type}", query)
14
+ end
15
+
16
+ def remove_resource(type, id)
17
+ check_type type
18
+ request(:delete, "/#{type}/#{id}")
19
+ end
20
+
21
+ def get_resource(type)
22
+ check_type type
23
+ return request(:get, "/#{type}/all")
24
+ end
25
+
26
+ def resource_exists?(type, name)
27
+ resource = get_resource_by_type_and_name(type, name)
28
+ return (resource && resource != [])
29
+ end
30
+
31
+ def get_resource_by_type_and_name(type, name = nil)
32
+ resources = get_resource(type)
33
+ if name && name != ''
34
+ filter = [*name].join("|")
35
+
36
+ filtered_resources = resources.select do |resource|
37
+ resource['Name'] =~ /#{filter}/
38
+ end
39
+
40
+ if filtered_resources.any?
41
+ Log.info "#{filtered_resources.count} resources found with filter #{filter}"
42
+ filtered_resources
43
+ else
44
+ Log.info "No #{type} found with filter #{filter}"
45
+ return nil
46
+ end
47
+ else
48
+ resources
49
+ end
50
+ end
51
+
52
+ def check_type(type)
53
+ raise NameError, "Invalid resource type supplied: '#{type}'" unless RESOURCE_TYPES.include? type
54
+ end
55
+ end
56
+
@@ -0,0 +1,40 @@
1
+ class TeamcityApi
2
+ include API
3
+
4
+ def initialize
5
+ create_api( ConfigStore.teamcity )
6
+ end
7
+
8
+ def build_queue
9
+ request(:get, 'buildQueue')
10
+ end
11
+
12
+ def create_build(build_config_id, properties={})
13
+ properties_string = ""
14
+ properties.each_pair do |k,v|
15
+ properties_string << "<property name='#{k}' value='#{v}'/>\n"
16
+ end
17
+ data = "
18
+ <build>
19
+ <buildType id='#{build_config_id}'/>
20
+ <properties>
21
+ #{properties_string}
22
+ </properties>
23
+ </build>
24
+ "
25
+ request(:post, 'buildQueue', data, "xml")
26
+ end
27
+
28
+ def set_project_parameter(project_id,parameter,value)
29
+ request(:put, "projects/#{project_id}/parameters/#{parameter}", value, "text")
30
+ end
31
+
32
+ def projects(parent_id=nil)
33
+ list = Nokogiri::XML.parse(request(:get, 'projects').body)
34
+ if parent_id
35
+ (list.xpath '//project').select {|e| e.attributes['parentProjectId'].to_s == parent_id}
36
+ else
37
+ list
38
+ end
39
+ end
40
+ end