a2zdeploy 1.0.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.
@@ -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