a2zdeploy 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
1
+ =begin
2
+
3
+ Uses project dependency map and configuration to process a DataPlatform Service's
4
+ code repository level framework upgrade and service deployments
5
+
6
+ =end
7
+
8
+ require_relative 'github_api'
9
+ require 'json'
10
+
11
+ class UpgradeAll
12
+
13
+ VERSION_MAP_FILE = 'versionmap.json'
14
+ MANIFEST_FILE = 'manifest.json'
15
+
16
+ # JSON files converted to hash
17
+ attr_accessor :version_map, :manifest
18
+
19
+ # repo_url is where the last known version map and manifest are checked-in
20
+ def initialize repo_url, branch
21
+
22
+ @repo_url = repo_url
23
+ @branch = branch
24
+
25
+ end
26
+
27
+ def retrieve_artifacts
28
+
29
+ return if !GithubApi.CheckoutRepoAfresh @repo_url, @branch
30
+
31
+ if File.exist? VERSION_MAP_FILE
32
+ @version_map = JSON.parse File.read(VERSION_MAP_FILE)
33
+ end
34
+ if File.exist? MANIFEST_FILE
35
+ @manifest = JSON.parse File.read(MANIFEST_FILE)
36
+ end
37
+
38
+ end
39
+
40
+ def Do
41
+
42
+ return if @version_map.nil || @manifest.nil
43
+
44
+ # retrieve version map and upgrade manifest
45
+ retrieve_artifacts
46
+
47
+ # if no changes exist, kick off deploy cycle only
48
+
49
+ # if changes exist, cycle through dependency tree and kick off upgrades
50
+
51
+ # either cycle was unterrupted, a step in upgrade failed or full cycle successfully completed
52
+ # save the version map and manifest
53
+
54
+ end
55
+
56
+ def version_diff
57
+
58
+ # create version map afresh to compare
59
+ vm = VersionMap.new
60
+ repo_url = @manifest['version_source']
61
+ versions = vm.version_map repo_url, 'master'
62
+
63
+ # If remote version doesn't exist, save it
64
+ if @version_map.nil
65
+
66
+ end
67
+
68
+ # compare current and remote versions, obtain changeset
69
+ end
70
+
71
+ end
@@ -0,0 +1,266 @@
1
+ require 'net/http'
2
+ require 'pathname'
3
+
4
+ # url template of file to check for version info with repo, branch and file path to replace
5
+ URLTEMPLATE ||= "%s/raw/%s/%s"
6
+
7
+ def get_file_list path, extension
8
+ return Dir.glob("#{path}/*.#{extension}")
9
+ end
10
+
11
+ def get_repo_version semver
12
+ remote_name = get_remote_name
13
+ branch_name = get_branch_name
14
+ url = URLTEMPLATE % [remote_name, branch_name, semver]
15
+ page_content = open(url)
16
+ repo_version = get_semver_version(page_content)
17
+ return repo_version
18
+ end
19
+
20
+ def get_current_version path, semver
21
+ location = File.join path, semver
22
+ content = File.read(location)
23
+ current_version = get_semver_version(content)
24
+ return current_version
25
+ end
26
+
27
+ def open(url)
28
+ Net::HTTP.get(URI.parse(url))
29
+ end
30
+
31
+ def get_semver_version(content)
32
+ s = content.split("\n")
33
+ val = ''
34
+ s.each{ |i|
35
+ if (i.start_with?(':'))
36
+ x = i.split(': ')[1].strip
37
+ x.gsub!("'", '')
38
+ if (x.length != 0)
39
+ val = val + x.to_s + '.'
40
+ end
41
+ end
42
+ }
43
+ val.chomp('.')
44
+ end
45
+
46
+ def same_versions version1, version2
47
+ return version1 == version2
48
+ end
49
+
50
+ def is_team_city_run
51
+ return ENV['BUILD_VCS_NUMBER'] != nil
52
+ end
53
+
54
+ #desc 'Check that Semver version bump task should not be invoked from teamcity'
55
+ def get_branch_name
56
+ current_branch = ENV['GIT_BRANCH_NAME']
57
+ if current_branch.nil?
58
+ raise '********GIT_BRANCH_NAME environment variable required for local rake run. Cannot continue with build. Exiting..............********'
59
+ else
60
+ return current_branch
61
+ end
62
+ end
63
+
64
+ def get_remote_name
65
+ remote_name = ENV['GIT_REMOTE_NAME']
66
+ if remote_name.nil?
67
+ raise '********GIT_REMOTE_NAME environment variable required for local rake run. Cannot continue with build. Exiting..............********'
68
+ else
69
+ @remote = `git remote -v`.split("\n")
70
+ @remote.each { |val|
71
+ if val =~ /#{remote_name}/
72
+ return val.split(" ")[1]
73
+ end
74
+ }
75
+ end
76
+ end
77
+
78
+ def fetch_updated_semver
79
+
80
+ updated_semver = get_update_semver_file_with_versions
81
+
82
+ #for local run user did not increment any semver
83
+ if updated_semver[3] == 0
84
+ puts '*********** No semver incremented detected. ***********'
85
+ return nil
86
+ end
87
+
88
+ version_remote = updated_semver[1].split('.')
89
+ version_local = updated_semver[2].split('.')
90
+
91
+ incremented = is_semver_incremented version_remote, version_local
92
+
93
+ [updated_semver[0].gsub("#{VERSIONING}/", ''), incremented]
94
+ end
95
+
96
+ def is_semver_incremented version_remote, version_local
97
+ is_incremented = [version_local[0].to_i - version_remote[0].to_i > 0, version_local[1].to_i - version_remote[1].to_i > 0, version_local[2].to_i - version_remote[2].to_i > 0]
98
+
99
+ if is_incremented[0]
100
+ if (version_local[1] != 0 || version_local[2] != 0)
101
+ raise 'Error - major version is incremented. Minor and Patch should be 0. Cannot continue build. Exiting....'
102
+ end
103
+ end
104
+
105
+ if is_incremented[1]
106
+ if is_incremented[0]
107
+ raise 'Error - cannot increment both major and minor version. . Exiting....'
108
+ end
109
+ end
110
+
111
+ if is_incremented[2]
112
+ if(is_incremented[0] || is_incremented[1])
113
+ raise 'Error - cannot increment major, minor and patch together. . Exiting....'
114
+ end
115
+ end
116
+
117
+ if is_incremented[0]
118
+ return 'major'
119
+ elsif is_incremented[1]
120
+ return 'minor'
121
+ elsif is_incremented[2]
122
+ return 'patch'
123
+ end
124
+ end
125
+
126
+ def get_update_semver_file_with_versions
127
+ count = 0
128
+ semver_file = ''
129
+ version_remote = ''
130
+ version_local = ''
131
+
132
+ semvers = get_file_list VERSIONING, 'semver'
133
+ if(semvers.empty?)
134
+ raise "************Error - no semver files found at #{VERSIONING}********"
135
+ end
136
+
137
+ semvers.each { |semver|
138
+ v1 = get_repo_version semver
139
+ v2 = get_current_version VERSIONING, semver.gsub("#{VERSIONING}/", '')
140
+
141
+ if (!same_versions(v1, v2))
142
+ puts "#{semver} version changed"
143
+ semver_file = semver
144
+ version_remote = v1
145
+ version_local = v2
146
+ count = count + 1
147
+ end
148
+ }
149
+ if count > 1
150
+ raise '*********** Error - Version increment is expected in a single semver file and can be only one of major|minor|patch ************'
151
+ return
152
+ end
153
+
154
+ [semver_file, version_remote, version_local, count]
155
+ end
156
+
157
+
158
+ def get_project_dependencies csproj
159
+ proj_files = Dir.glob '**/*.csproj'
160
+ @unique_list = Array.new
161
+ proj_files.each{ |file|
162
+ doc = Nokogiri::XML File.read file
163
+ nodes = doc.search 'ProjectReference'
164
+ nodes.each { |node|
165
+ ref_val = node['Include']
166
+ id = ref_val.split('\\')[2]
167
+ if id.eql? csproj
168
+ @unique_list << file.split('/')[2]
169
+ end
170
+ }
171
+ }
172
+ @unique_list.uniq
173
+ end
174
+
175
+ def get_project_dependencies_semver_files projects_dependencies, semver_project_map
176
+ project_semver_map = semver_project_map.invert
177
+ @values = Array.new
178
+ projects_dependencies.each { |dependency|
179
+ if semver_project_map.value?(dependency)
180
+ @values << project_semver_map[dependency]
181
+ end
182
+ }
183
+ @values
184
+ end
185
+
186
+ def load_semver path
187
+ v = SemVer.new
188
+ v.load path
189
+ return v
190
+ end
191
+
192
+ # Increment the major, minor, or patch of the .semver file.
193
+ def increment_version semver_file, dimension
194
+ if dimension.nil?
195
+ raise 'The parameter dimension is required to be one of : major | minor | patch'
196
+ end
197
+ semver = load_semver semver_file
198
+
199
+ case dimension
200
+ when 'major'
201
+ semver.major += 1
202
+ semver.minor = 0
203
+ semver.patch = 0
204
+ when 'minor'
205
+ semver.minor += 1
206
+ semver.patch = 0
207
+ when 'patch'
208
+ semver.patch += 1
209
+ else
210
+ raise "The parameter #{dimension} is invalid: major | minor | patch"
211
+ end
212
+
213
+ hash = "---\n:major: %s\n:minor: %s\n:patch: %s\n:special: '%s'\n:metadata: '%s'"
214
+ value = hash % [semver.major, semver.minor, semver.patch, semver.special, semver.metadata]
215
+ File.write semver_file, value
216
+ end
217
+
218
+ def create_semver_project_map product, semver_location
219
+ semver_project_map = Hash.new
220
+
221
+ rake_list = get_file_list semver_location, 'semver'
222
+ rake_list.each{ |rakefile|
223
+ rake = rakefile.gsub("#{semver_location}/", '')
224
+ project_name = rake.gsub('.semver', '')
225
+ semver_project_map[rake] = "#{product}.#{project_name}.csproj"
226
+ }
227
+ semver_project_map
228
+ end
229
+
230
+ def display_local_changes_to_user
231
+ git_status = `git status -s`
232
+ if (git_status != nil || git_status != GlobalConstants::EMPTY)
233
+ puts '********** Local semver/s updated. Please commit the change.'
234
+ end
235
+ end
236
+
237
+ def update_dependent_semvers product, semver_location, semver_file, dimension
238
+ semver_project_map = create_semver_project_map product, semver_location
239
+ projects_dependencies = get_project_dependencies semver_project_map[semver_file]
240
+ semver_files = get_project_dependencies_semver_files projects_dependencies, semver_project_map
241
+ semver_files.each { |semver|
242
+ increment_version File.join(semver_location, semver), dimension
243
+ }
244
+ end
245
+
246
+ def auto_update_semver project_name, semver_location, semver_file, semver_dimension
247
+ Dir.chdir project_name if File.basename(Dir.pwd) != project_name
248
+ if(semver_file.nil? || semver_file.strip == '' || semver_file == '.semver')
249
+ increment_version '.semver', semver_dimension
250
+ else
251
+ increment_version File.join(semver_location, semver_file), semver_dimension
252
+ update_dependent_semvers project_name, semver_location, semver_file, semver_dimension
253
+ end
254
+ end
255
+
256
+ def auto_update_local_semver
257
+ if(!Dir.exist?(VERSIONING) || File.exist?('.semver'))
258
+ raise 'Error - local upgrade process only applies to projects with multiple semvers. If you have one semver at root level, please update it manually if needed. Do not invoke auto_update_local_semver task.'
259
+ end
260
+ updated_semver_with_version_state = fetch_updated_semver
261
+ if updated_semver_with_version_state != nil
262
+ update_dependent_semvers PRODUCT, VERSIONING, updated_semver_with_version_state[0], updated_semver_with_version_state[1]
263
+ end
264
+
265
+ display_local_changes_to_user
266
+ end
@@ -0,0 +1,98 @@
1
+ =begin
2
+ Use case #1:
3
+ Generate and returns a unique list of all packages and their versions for a given repository
4
+ Example:
5
+ 10.0.0(obtained from semver) version map will look like:
6
+ {
7
+ "RelayHealth.DataPlatform.Framework" => {
8
+ "Version" => "10.0.0"
9
+ "RelayHealth.DataPlatform.Contracts" => "10.0.0",
10
+ "Windows.Azure.Storage" => "4.3.0"
11
+ ...
12
+ }
13
+ }
14
+ When solution uses using individual package versioning, version map still like above because framework forms the root of all dependencies.
15
+
16
+ This can be maintained static by parsing once and saving to a github repo or can be done realtime.
17
+ Processing time is minimal, DP repo checkout time may actually be longer.
18
+
19
+ 1. Checkout repo
20
+ 2. Scan packages.config files for packages and versions and store in array
21
+ 3. Uniquefy list to a hash map and return
22
+ =end
23
+
24
+ require 'json'
25
+ require 'nokogiri'
26
+ require 'semver'
27
+ require_relative 'github_api'
28
+ require_relative 'version'
29
+
30
+ class VersionMap
31
+
32
+ VERSIONMAPFILE = 'versionmap.json'
33
+
34
+ def version_map repo_url, branch
35
+ return if repo_url.to_s.strip.length == 0
36
+ return if branch.to_s.strip.length == 0
37
+
38
+ return if !GithubApi.CheckoutRepoAfresh repo_url, branch
39
+
40
+ # load old versions
41
+ old_versions = {}
42
+ if (File.exists? VERSIONMAPFILE)
43
+ old_versions = JSON.parse File.read(VERSIONMAPFILE)
44
+ end
45
+
46
+ # grab all packages.config files
47
+ versions = {}
48
+ pkg_files = Dir.glob '**/packages.config'
49
+ pkg_files.each{ |file|
50
+ puts "Finding packages in: #{file}"
51
+ doc = Nokogiri::XML File.read(file)
52
+ nodes = doc.xpath "//*[@id]"
53
+ nodes.each { |node|
54
+ if (!versions[node['id']].nil? && node['version'] != versions[node['id']]['version'])
55
+ puts "======Error: Package #{node['id']} with version #{node['version']} has a different pre-exisiting version: #{versions[node['id']]['version']} in #{versions[node['id']]['file']}"
56
+ end
57
+ versions[node['id']] = node['version']
58
+ }
59
+ }
60
+
61
+ update_platform_package_versions versions
62
+
63
+ Dir.chdir '..'
64
+ File.write VERSIONMAPFILE, versions.to_json
65
+
66
+ versions
67
+
68
+ end
69
+
70
+ # these versions will need to be replaced with project specific semvers
71
+ def update_platform_package_versions versions
72
+ versions['RelayHealth.DataPlatform.Contracts'] = get_semver 'Contracts.semver'
73
+ versions['RelayHealth.DataPlatform.Framework'] = get_semver 'Framework.semver'
74
+ versions['RelayHealth.DataPlatform.Framework.Messaging'] = get_semver 'Framework.Messaging.semver'
75
+ versions['RelayHealth.DataPlatform.Framework.Web'] = get_semver 'Framework.Web.semver'
76
+ versions['RelayHealth.DataPlatform.Identity'] = get_semver 'Identity.semver'
77
+ versions['RelayHealth.DataPlatform.Management'] = get_semver 'Management.semver'
78
+ versions['RelayHealth.DataPlatform.Management.Tools'] = get_semver 'Management.semver'
79
+ versions['RelayHealth.DataPlatform.Test'] = get_semver 'Test.semver'
80
+ versions['RelayHealth.DataPlatform.Test.Messaging'] = get_semver 'Test.Messaging.semver'
81
+ versions['RelayHealth.DataPlatform.Test.Bvt'] = get_semver 'Test.Bvt.semver'
82
+ versions['RelayHealth.DataPlatform.Runtime'] = get_semver 'Runtime.semver'
83
+ versions['RelayHealth.DataPlatform.Runtime.Host'] = get_semver 'Runtime.semver'
84
+
85
+ versions
86
+ end
87
+ end
88
+
89
+ def get_semver semver
90
+ ver = load_semver File.join('versioning', semver)
91
+ v = ver.to_s.sub 'v', '' # removes 'v' at start of string which isn't expected for version spec in .package and .csproj files
92
+ v
93
+ end
94
+
95
+ #puts "test"
96
+ #vm = VersionMap.new
97
+ #versions = vm.version_map "http://", "master"
98
+ #version_map("http://ndhaxpgit01.mckesson.com/Carnegie/RelayHealth.DataPlatform.git", "master")
@@ -0,0 +1,7 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.warning = true
5
+ t.verbose = true
6
+ t.test_files = FileList['spec/*_spec.rb']
7
+ end
@@ -0,0 +1,93 @@
1
+ require_relative '../dependency_tree'
2
+ require_relative '../globalconstants'
3
+ require 'test/unit'
4
+
5
+ class TestDependencyTree < Test::Unit::TestCase
6
+
7
+ ONTOLOGY = 'Ontology'
8
+ FHIRWALKER = 'FhirWalker'
9
+ FHIRSERVICE = 'FhirService'
10
+
11
+ def test_empty_tree_returns_nil_for_root
12
+ depends = DependencyTree.new(nil)
13
+ assert_equal(nil, depends.root)
14
+ end
15
+
16
+ def test_empty_tree_returns_nil_for_next_if_current_nil
17
+ depends = DependencyTree.new(nil)
18
+ assert_equal(nil, depends.next_node(nil))
19
+ end
20
+
21
+ def test_empty_tree_returns_nil_for_next
22
+ depends = DependencyTree.new(nil)
23
+ assert_equal(nil, depends.next_node('src'))
24
+ end
25
+
26
+ def test_empty_tree_returns_nil_for_previous_if_current_nil
27
+ depends = DependencyTree.new(nil)
28
+ assert_equal(nil, depends.previous_node(nil))
29
+ end
30
+
31
+ def test_empty_tree_returns_nil_for_previous
32
+ depends = DependencyTree.new(nil)
33
+ assert_equal(nil, depends.previous_node('src'))
34
+ end
35
+
36
+ def test_tree_returns_nil_for_root_previous
37
+ depends = DependencyTree.new(GlobalConstants::ROOT => {GlobalConstants::PROJECT => ONTOLOGY, GlobalConstants::PREVIOUS => nil})
38
+ assert_equal(nil, depends.previous_node(GlobalConstants::ROOT))
39
+ end
40
+
41
+ def test_root_node_found
42
+ depends = DependencyTree.new(GlobalConstants::ROOT => {GlobalConstants::PROJECT => ONTOLOGY})
43
+ assert_equal(ONTOLOGY, depends.root)
44
+ end
45
+
46
+ def test_next_node_found
47
+ depends = DependencyTree.new(GlobalConstants::ROOT => {GlobalConstants::PROJECT => ONTOLOGY, GlobalConstants::NEXT => FHIRWALKER})
48
+ assert_equal(FHIRWALKER, depends.next_node(GlobalConstants::ROOT))
49
+ end
50
+
51
+ def test_prev_node_found
52
+ depends = DependencyTree.new(GlobalConstants::ROOT => {GlobalConstants::PROJECT => FHIRWALKER, GlobalConstants::PREVIOUS => ONTOLOGY, GlobalConstants::NEXT => FHIRSERVICE})
53
+ assert_equal(ONTOLOGY, depends.previous_node(GlobalConstants::ROOT))
54
+ end
55
+
56
+ def test_root_node_not_found
57
+ depends = DependencyTree.new(GlobalConstants::ROOT => {GlobalConstants::PROJECT => ONTOLOGY})
58
+ assert_not_equal(GlobalConstants::ROOT, depends.root)
59
+ end
60
+
61
+ def test_next_node_not_found
62
+ depends = DependencyTree.new(GlobalConstants::ROOT => {GlobalConstants::PROJECT => ONTOLOGY, GlobalConstants::NEXT => FHIRSERVICE})
63
+ assert_not_equal(FHIRWALKER, depends.next_node(GlobalConstants::ROOT))
64
+ end
65
+
66
+ def test_prev_node_not_found
67
+ depends = DependencyTree.new(GlobalConstants::ROOT => {GlobalConstants::PROJECT => FHIRWALKER, GlobalConstants::PREVIOUS => GlobalConstants::ROOT, GlobalConstants::NEXT => FHIRSERVICE})
68
+ assert_not_equal(ONTOLOGY, depends.previous_node(GlobalConstants::ROOT))
69
+ end
70
+
71
+ def test_traverse
72
+ depends = DependencyTree.new(
73
+ GlobalConstants::ROOT => {
74
+ GlobalConstants::PROJECT => ONTOLOGY,
75
+ GlobalConstants::PREVIOUS => nil,
76
+ GlobalConstants::NEXT => FHIRWALKER
77
+ },
78
+ FHIRWALKER => {
79
+ GlobalConstants::PROJECT => FHIRWALKER,
80
+ GlobalConstants::PREVIOUS => GlobalConstants::ROOT,
81
+ GlobalConstants::NEXT => FHIRSERVICE
82
+ }
83
+ )
84
+
85
+ expected = [ONTOLOGY, FHIRWALKER]
86
+ actual = []
87
+ depends.traverse do |node|
88
+ actual << node
89
+ end
90
+
91
+ assert_equal expected, actual
92
+ end
93
+ end