qcloudhive 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.idea/.rakeTasks +7 -0
  3. data/.idea/Hive.iml +54 -0
  4. data/.idea/dictionaries/dongzhao.xml +7 -0
  5. data/.idea/misc.xml +4 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/vcs.xml +6 -0
  8. data/.idea/workspace.xml +699 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/Documents/Images/93A5AC58-17D5-4D3F-B5A8-E509CF429BD0.png +0 -0
  11. data/Documents/Images/C702C824-11B1-47A8-BB15-9F6F14F2B649.png +0 -0
  12. data/Gem/qcloudhive/.idea/misc.xml +4 -0
  13. data/Gem/qcloudhive/.idea/modules.xml +8 -0
  14. data/Gem/qcloudhive/.idea/qcloudhive.iml +8 -0
  15. data/Gem/qcloudhive/.idea/workspace.xml +383 -0
  16. data/Gemfile +4 -0
  17. data/Gemfile.lock +122 -0
  18. data/LICENSE.txt +21 -0
  19. data/README.md +273 -0
  20. data/Rakefile +5 -0
  21. data/bin/console +14 -0
  22. data/bin/setup +8 -0
  23. data/exe/Hive +302 -0
  24. data/lib/qcloudhive.rb +16 -0
  25. data/lib/qcloudhive/config.rb +176 -0
  26. data/lib/qcloudhive/feature.rb +92 -0
  27. data/lib/qcloudhive/framework.rb +192 -0
  28. data/lib/qcloudhive/git_helper.rb +75 -0
  29. data/lib/qcloudhive/gitlab.rb +69 -0
  30. data/lib/qcloudhive/manifest.rb +346 -0
  31. data/lib/qcloudhive/module.rb +228 -0
  32. data/lib/qcloudhive/pod_helper.rb +119 -0
  33. data/lib/qcloudhive/product.rb +32 -0
  34. data/lib/qcloudhive/project.rb +108 -0
  35. data/lib/qcloudhive/repo.rb +177 -0
  36. data/lib/qcloudhive/spec_helper.rb +45 -0
  37. data/lib/qcloudhive/utils.rb +46 -0
  38. data/lib/qcloudhive/version.rb +3 -0
  39. data/lib/qcloudhive/xcodeproj.rb +163 -0
  40. data/qcloudhive.gemspec +43 -0
  41. data/resources/shellscriptes/HiveApp.sh +156 -0
  42. data/resources/shellscriptes/HiveFramework +158 -0
  43. data/resources/shellscriptes/build_framework.sh +76 -0
  44. data/resources/templates/AppDefaultTemplate/.gitignore +41 -0
  45. data/resources/templates/AppDefaultTemplate/AppDefaultTemplate.xcodeproj/project.pbxproj +290 -0
  46. data/resources/templates/AppDefaultTemplate/AppDefaultTemplate.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  47. data/resources/templates/AppDefaultTemplate/AppDefaultTemplate/Info.plist +24 -0
  48. data/resources/templates/AppDefaultTemplate/Podfile +9 -0
  49. data/resources/templates/HiveAppTemplate/.gitignore +41 -0
  50. data/resources/templates/HiveAppTemplate/HiveAppTemplate.xcodeproj/project.pbxproj +539 -0
  51. data/resources/templates/HiveAppTemplate/HiveAppTemplate.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  52. data/resources/templates/HiveAppTemplate/HiveAppTemplate/AppDelegate.h +17 -0
  53. data/resources/templates/HiveAppTemplate/HiveAppTemplate/AppDelegate.m +51 -0
  54. data/resources/templates/HiveAppTemplate/HiveAppTemplate/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  55. data/resources/templates/HiveAppTemplate/HiveAppTemplate/Base.lproj/LaunchScreen.storyboard +27 -0
  56. data/resources/templates/HiveAppTemplate/HiveAppTemplate/Base.lproj/Main.storyboard +26 -0
  57. data/resources/templates/HiveAppTemplate/HiveAppTemplate/Info.plist +45 -0
  58. data/resources/templates/HiveAppTemplate/HiveAppTemplate/ViewController.h +15 -0
  59. data/resources/templates/HiveAppTemplate/HiveAppTemplate/ViewController.m +29 -0
  60. data/resources/templates/HiveAppTemplate/HiveAppTemplate/main.m +16 -0
  61. data/resources/templates/HiveAppTemplate/HiveAppTemplateTests/HiveAppTemplateTests.m +39 -0
  62. data/resources/templates/HiveAppTemplate/HiveAppTemplateTests/Info.plist +22 -0
  63. data/resources/templates/HiveAppTemplate/HiveAppTemplateUITests/HiveAppTemplateUITests.m +40 -0
  64. data/resources/templates/HiveAppTemplate/HiveAppTemplateUITests/Info.plist +22 -0
  65. data/resources/templates/HiveAppTemplate/Podfile +20 -0
  66. data/resources/templates/commonConf.sh +9 -0
  67. data/resources/templates/manifests/build.rb +8 -0
  68. data/resources/templates/manifests/default.xml +12 -0
  69. data/resources/templates/template.podspec +37 -0
  70. metadata +321 -0
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rake'
3
+ require 'optparse'
4
+ require 'pathname'
5
+ require 'uri'
6
+ require 'cocoapods'
7
+ require 'git'
8
+ require 'xcodeproj'
9
+ require 'parseconfig'
10
+ require 'qcloudhive/config'
11
+ require 'qcloudhive'
12
+ require 'qcloudhive/version'
13
+ require 'qcloudhive/project'
14
+ require 'qcloudhive/framework'
15
+ require 'qcloudhive/gitlab'
16
+ require 'qcloudhive/utils'
17
+ require 'qcloudhive/product'
18
+ require 'qcloudhive/manifest'
19
+ require 'qcloudhive/module'
20
+ require 'qcloudhive/repo'
21
+ require 'qcloudhive/xcodeproj'
22
+ require "qcloudhive/feature"
23
+ QCloudHive::Config.setup
24
+
25
+
26
+
27
+ # moudle commands
28
+ CMD_MODULE = "module"
29
+ CMD_MODULE_ADD = "add"
30
+ CMD_MODULE_REALASE = "release"
31
+ CMD_MODULE_DISLINK = 'dislink'
32
+ CMD_MODULE_GENERATE_CODE = "generate"
33
+
34
+ # manifest commands
35
+ CMD_MANIFEST = "manifest"
36
+ CMD_MANIFEST_PUSH = "push"
37
+
38
+ # framework commands
39
+ CMD_FRAMEWORK = "framework"
40
+ CMD_FRAMEWORK_BUILD = "build"
41
+
42
+ # product commands
43
+ CMD_PRODUCT = "product"
44
+ CMD_PRODUCT_INIT = "init"
45
+ CMD_PRODUCT_UPDATE = 'update'
46
+ CMD_PRODUCT_BUILD = 'build'
47
+
48
+
49
+ # project command
50
+ CMD_PROJECT = "project"
51
+ CMD_PROJECT_INIT = "init"
52
+
53
+
54
+ # repo command
55
+ CMD_REPO = "repo"
56
+ CMD_REPO_STATUS = "status"
57
+ CMD_REPO_REQUEST = 'request'
58
+
59
+ # feature command
60
+ CMD_FEATURE = "feature"
61
+ CMD_FEATURE_LIST = "list"
62
+ CMD_FEATURE_START = "start"
63
+ CMD_FEATURE_COMMIT = "commit"
64
+ CMD_FEATURE_PUSH = "push"
65
+ CMD_FEATURE_CHECKDEPENDENCY = "dependency"
66
+
67
+
68
+ puts "Hive-V:#{QCloudHive::VERSION} "
69
+ optionParser = OptionParser.new do |opts|
70
+ opts.banner = 'Hive is a toolkit for mananger iOS multi modules, it base google-repo & git & cocoapods'
71
+ opts.on('-h', '--help', 'Displays Help') do
72
+ puts opts
73
+ puts '''
74
+ Please use sub commnd:
75
+ create-framework create a module
76
+ push-manifest push the manifest files
77
+ init 创建新的产品,空
78
+ build-framework 打包framework
79
+ add-module add exist module
80
+ '''
81
+ exit
82
+ end
83
+ end
84
+
85
+ options = {}
86
+ #default value
87
+ # options[:c_repo] = false
88
+ REPO_COMMANDS = {
89
+ CMD_REPO_STATUS => OptionParser.new do |opts|
90
+ end ,
91
+ CMD_REPO_REQUEST => OptionParser.new do |opts|
92
+ opts.on("-m message", "--message Message", "the commit message") do |v|
93
+ options[:i_message] = v
94
+ end
95
+ end
96
+ }
97
+
98
+ MANIFEST_COMMANDS = {
99
+ CMD_MANIFEST_PUSH => OptionParser.new do |opts|
100
+ end
101
+ }
102
+ FRAMEWORK_COMMANDS = {
103
+ CMD_FRAMEWORK_BUILD => OptionParser.new do |opts|
104
+ opts.on("-n NAME", "--name Name", "the project name") do |v|
105
+ options[:i_name] = v
106
+ end
107
+ opts.on("-u a,b,c","--unlink a,b,c", Array, "Array unlined frameworks or modules") { |list|
108
+ options[:i_unlinked_frameworks] = list
109
+ }
110
+ opts.on("--[no-]sim", "build similuator arch") { |value|
111
+ options[:i_build_sim] = value
112
+ }
113
+ end,
114
+ }
115
+
116
+
117
+ PRODUCST_COMMANDS = {
118
+ CMD_PRODUCT_INIT => OptionParser.new do |opts|
119
+ opts.banner = "Create A Demo App (empty)"
120
+ opts.on("-n NAME", "--name Name", "the module name") do |v|
121
+ options[:c_name] = v
122
+ end
123
+ opts.on("-u URL", "--url url", "the git remote url") do |v|
124
+ options[:c_url] = v
125
+ end
126
+ end,
127
+ CMD_PRODUCT_UPDATE => OptionParser.new do |opts|
128
+ opts.banner = "update"
129
+ opts.on("-l" , "--library", "use static library ") do
130
+ L.info("setup use framework false")
131
+ L.info("Config #{QCloudHive::Config}")
132
+ QCloudHive::Config.useFrameworks = false
133
+ end
134
+ end,
135
+ CMD_PRODUCT_BUILD => OptionParser.new do |opts|
136
+ end
137
+ }
138
+
139
+ PROJECT_COMMANDS = {
140
+ CMD_PROJECT_INIT => OptionParser.new do |opts|
141
+ opts.on("-n NAME", "--name Name", "the project name") do |v|
142
+ options["name"] = v
143
+ end
144
+ end,
145
+ }
146
+
147
+ MODULE_COMMANDS = {
148
+ CMD_MODULE_ADD => OptionParser.new do |opts|
149
+ opts.banner = "Add a exist modules for ul"
150
+ opts.on("-n NAME", "--name Name", "the module name") do |v|
151
+ options[:c_name] = v
152
+ end
153
+ opts.on("-u URL", "--url url", "the git remote url") do |v|
154
+ options[:c_url] = v
155
+ end
156
+ opts.on("-p PATH", "--repo-path Path", "repo local relevate path") do |v|
157
+ options[:c_rpath] = v
158
+ end
159
+ end,
160
+ CMD_MODULE_REALASE => OptionParser.new do |opts|
161
+
162
+ end,
163
+
164
+ CMD_MODULE_DISLINK => OptionParser.new do |opts|
165
+ opts.banner = "delete a module form manifest"
166
+ opts.on("-n NAME", "--name Name", "the project name") do |v|
167
+ options[:i_name] = v
168
+ end
169
+ end,
170
+
171
+ CMD_MODULE_GENERATE_CODE => OptionParser.new do |opts|
172
+
173
+ end,
174
+ }
175
+
176
+ FEATURE_COMMANDS = {
177
+ CMD_FEATURE_LIST => OptionParser.new do |opts|
178
+ opts.banner = "delete a module form manifest"
179
+ opts.on("-n NAME", "--name Name", "the project name") do |v|
180
+ options[:i_name] = v
181
+ end
182
+ end,
183
+ CMD_FEATURE_START => OptionParser.new do |opts|
184
+ opts.banner = "delete a module form manifest"
185
+ opts.on("-n NAME", "--name Name", "the project name") do |v|
186
+ options[:i_name] = v
187
+ end
188
+ end,
189
+ CMD_FEATURE_COMMIT => OptionParser.new do |opts|
190
+ opts.banner = "delete a module form manifest"
191
+ opts.on("-m message", "--message Message", "the commit message") do |v|
192
+ options[:i_message] = v
193
+ end
194
+ end,
195
+ CMD_FEATURE_PUSH => OptionParser.new do |opts|
196
+ end,
197
+ CMD_FEATURE_CHECKDEPENDENCY =>OptionParser.new do |opts|
198
+ end,
199
+ }
200
+
201
+
202
+ GLOBAL_COMMANDS = {
203
+ CMD_MANIFEST => MANIFEST_COMMANDS,
204
+ CMD_PROJECT => PROJECT_COMMANDS,
205
+ CMD_MODULE => MODULE_COMMANDS,
206
+ CMD_PRODUCT => PRODUCST_COMMANDS,
207
+ CMD_FRAMEWORK => FRAMEWORK_COMMANDS,
208
+ CMD_REPO => REPO_COMMANDS,
209
+ CMD_FEATURE => FEATURE_COMMANDS,
210
+ }
211
+
212
+
213
+
214
+ # begin parse and exe
215
+ optionParser.order!
216
+ command = ARGV.shift
217
+ if command == nil
218
+ L.error "Please input sub command"
219
+ exit(1)
220
+ end
221
+ cmd = GLOBAL_COMMANDS[command]
222
+ if cmd == nil
223
+ L.error "Please input sub command, unkonw cmd [#{command}]"
224
+ exit(1)
225
+ end
226
+
227
+ if cmd.instance_of? OptionParser
228
+ cmd.order!
229
+ end
230
+
231
+ L.debug "Command: #{command} "
232
+
233
+ COMMAND_MAPPER = {
234
+ CMD_PROJECT => {
235
+ CMD_PROJECT_INIT => Proc.new do |c, opts|
236
+ QCloudHive::Project.init(c, opts)
237
+ end,
238
+ },
239
+ CMD_MODULE => {
240
+ CMD_MODULE_ADD => Proc.new do |c, opts|
241
+ QCloudHive::HiveModule.add(c,opts)
242
+ end,
243
+ CMD_MODULE_REALASE => Proc.new do |c, opts|
244
+ QCloudHive::HiveModule.release(c, opts)
245
+ end,
246
+ CMD_MODULE_DISLINK => Proc.new do |c, opts|
247
+ QCloudHive::HiveModule.dislink(c, opts)
248
+ end
249
+ },
250
+ CMD_MANIFEST => {
251
+ CMD_MANIFEST_PUSH => Proc.new do |c, opts|
252
+ QCloudHive.syncManifest
253
+ end
254
+ },
255
+ CMD_FRAMEWORK => {
256
+ CMD_FRAMEWORK_BUILD => Proc.new do |c, opts|
257
+ QCloudHive.BuildFramework(c, opts)
258
+ end
259
+ },
260
+ CMD_PRODUCT => {
261
+ CMD_PRODUCT_INIT => Proc.new do |c, opts|
262
+ QCloudHive.CreateAPP(c, opts)
263
+ end,
264
+ CMD_PRODUCT_UPDATE => Proc.new do |c, opts|
265
+ QCloudHive.UpdateProducts c, opts
266
+ end,
267
+ CMD_PRODUCT_BUILD => Proc.new do |c, opts|
268
+ QCloudHive.RemapProduct c, opts
269
+ end
270
+ },
271
+
272
+ CMD_REPO => {
273
+ CMD_REPO_STATUS => Proc.new do |c, opts|
274
+ QCloudHive::Repo.checkStatus(c, opts)
275
+ end,
276
+ CMD_REPO_REQUEST => Proc.new do |c, opts|
277
+ QCloudHive::Repo.request(c, opts)
278
+ end
279
+ },
280
+
281
+ CMD_FEATURE => {
282
+ CMD_FEATURE_COMMIT => Proc.new do |c, opts|
283
+ QCloudHive.commitFeature(c, opts)
284
+ end,
285
+ CMD_FEATURE_PUSH => Proc.new do |c, opts|
286
+ QCloudHive.pushFeature(c, opts)
287
+ end,
288
+ CMD_FEATURE_CHECKDEPENDENCY => Proc.new do |c, opts|
289
+ QCloudHive.checkDependencies(c, opts)
290
+ end,
291
+ }
292
+ }
293
+
294
+
295
+ if GLOBAL_COMMANDS[command].instance_of? Hash
296
+ subcommand = ARGV.shift
297
+ cmd = GLOBAL_COMMANDS[command][subcommand]
298
+ cmd.order!
299
+ method = COMMAND_MAPPER[command][subcommand]
300
+ L.debug "#{options}"
301
+ method.call(cmd , options)
302
+ end
@@ -0,0 +1,16 @@
1
+ require "qcloudhive/version"
2
+
3
+ module QCloudHive
4
+ require 'pathname'
5
+ require 'qcloudhive/version'
6
+ require 'qcloudhive/project'
7
+ require 'qcloudhive/framework'
8
+ require 'qcloudhive/config'
9
+ require 'qcloudhive/gitlab'
10
+ require 'qcloudhive/utils'
11
+ require 'qcloudhive/product'
12
+ require 'qcloudhive/manifest'
13
+ require 'qcloudhive/module'
14
+ require 'qcloudhive/repo'
15
+ require 'colorize'
16
+ end
@@ -0,0 +1,176 @@
1
+ require 'parseconfig'
2
+ require "pathname"
3
+ require_relative 'utils'
4
+
5
+
6
+ module QCloudHive
7
+ CODE_OA_GROUDP = "CodeOA"
8
+ CODE_OA_CONFIG_PRIVATE_TOKEN = "private_token"
9
+ CODE_OA_TEAM_NAME = "team"
10
+ REPO_ROOT_NAME = ".repo"
11
+ module Config
12
+ SHARE_CONFIG = "Share"
13
+ USE_FRAMEWORKS = "use_framework"
14
+ class << self
15
+ @@projectRootDirectory = nil
16
+ @@reporoot = nil
17
+ @@manifest = nil
18
+ @@setuped = false
19
+ attr_accessor :team
20
+ attr_accessor :cmdPath
21
+ attr_accessor :runPath
22
+ attr_accessor :namespaceID
23
+ attr_accessor :templateManifestPath
24
+ attr_accessor :templatesPath
25
+ attr_accessor :scriptesDirectory
26
+ attr_accessor :bitcodeEnable
27
+ attr_accessor :buildFromCommit
28
+ attr_accessor :useFrameworks
29
+ end
30
+
31
+ def Config.repoRoot()
32
+ if @@reporoot == nil
33
+ @@reporoot = QCloudHive.FindRepoRoot(Dir.pwd)
34
+ if @@reporoot == nil
35
+ Error "当前不再任何项目目录下面,请确认!!!"
36
+ end
37
+ end
38
+ return @@reporoot
39
+ end
40
+ def Config.dumpShareConfig
41
+ conf = "[#{SHARE_CONFIG}]
42
+ #{USE_FRAMEWORKS} = #{useFrameworks}"
43
+ file = Pathname(repoRoot()).join("share.conf").to_path
44
+ File.open(file, "w") { |f|
45
+ f.write conf
46
+ }
47
+ end
48
+ def Config.loadShareConfig
49
+ configFilePath = Pathname(repoRoot()).join("share.conf").to_path
50
+ if File.exist?(configFilePath) != true
51
+ return
52
+ end
53
+ config = ParseConfig.new(configFilePath)
54
+ L.info("config #{config} #{config.class}")
55
+ if not config.nil?
56
+ shareConfig = config[SHARE_CONFIG]
57
+ L.info("shareConfig #{shareConfig} #{shareConfig.class}")
58
+ if not shareConfig.nil?
59
+ useFramework = config[SHARE_CONFIG][USE_FRAMEWORKS]
60
+ if useFramework == "true"
61
+ Config.useFrameworks = true
62
+ elsif useFramework == "false"
63
+ Config.useFrameworks = false
64
+ end
65
+ L.info("#{useFramework} #{useFramework.class}")
66
+ end
67
+ end
68
+ end
69
+ def Config.projectRootDirectory()
70
+ if @@projectRootDirectory.nil?
71
+ @@projectRootDirectory = Pathname(repoRoot).parent.to_path
72
+ end
73
+ return @@projectRootDirectory
74
+ end
75
+ def Config.version()
76
+ return QCloudHive::VERSION
77
+ end
78
+
79
+ def Config.podSource()
80
+ Pod::Config::instance.sources_manager.aggregate
81
+ end
82
+
83
+ def Config.manifest()
84
+ if @@manifest == nil
85
+ manifestPath = repoRoot+ "manifests/default.xml"
86
+ L.debug "manifestPath #{manifestPath}"
87
+ @@manifest = Manifest.new(manifestPath)
88
+ end
89
+ return @@manifest
90
+ end
91
+
92
+ def Config.setup()
93
+ if @@setuped == true
94
+ return
95
+ end
96
+ @@setuped = true
97
+ Config.useFrameworks = true
98
+ configFilePath = Pathname("~/.hiveconfig").expand_path
99
+ if File.exist?(configFilePath) != true
100
+ raise "Hive 配置文件不存在,请检查"
101
+ end
102
+ config = ParseConfig.new(configFilePath)
103
+ if config[CODE_OA_GROUDP].nil?
104
+ Error "没有CodeOA的配置信息,请检查"
105
+ end
106
+ private_token = config[CODE_OA_GROUDP][CODE_OA_CONFIG_PRIVATE_TOKEN]
107
+ if private_token == nil
108
+ raise "没有设置CodeOA的private_token请检查并设置"
109
+ end
110
+ team = config[CODE_OA_GROUDP][CODE_OA_TEAM_NAME]
111
+ if team == nil
112
+ raise "没有设置Team Name请设置"
113
+ end
114
+ #log level config
115
+ hiveGroup = config["Hive"]
116
+ if not hiveGroup.nil?
117
+ logLevel = hiveGroup["loglevel"]
118
+ if logLevel.nil?
119
+ L.level = Logger::ERROR
120
+ elsif logLevel == "INFO"
121
+ L.level = Logger::INFO
122
+ elsif logLevel == "DEBUG"
123
+ L.level = Logger::DEBUG
124
+ elsif logLevel == "ERROR"
125
+ L.level = Logger::ERROR
126
+ end
127
+ end
128
+
129
+ puts "LogLevel ........ #{L.level} logLevel"
130
+ #code OA
131
+ CodeOA.setup(private_token)
132
+ Config.team = team
133
+ Config.cmdPath = Pathname.new(__FILE__).realpath.parent.parent.parent.to_path+"/"
134
+ Config.runPath = Dir.pwd + "/"
135
+ Config.templatesPath = Config.cmdPath+"resources/templates/"
136
+ Config.templateManifestPath = Config.templatesPath+"manifests/"
137
+ Config.scriptesDirectory = Config.cmdPath+"resources/shellscriptes/"
138
+ L.info "manifest path is #{Config.templateManifestPath}"
139
+ codeGroup = Gitlab.groups.select{|g| g.name == team}.first
140
+ if codeGroup == nil
141
+ Error("您不属于Group #{team}")
142
+ end
143
+ Config.namespaceID = codeGroup.id
144
+ @@reporoot = nil
145
+ @@manifest = nil
146
+ @@projectRootDirectory = nil
147
+ @@bitcodeEnable = false
148
+ @@buildFromCommit = false
149
+ end
150
+ end
151
+ def QCloudHive.FindRepoRoot(path)
152
+ pwd = Pathname(path)
153
+ if pwd.to_path == "/"
154
+ return nil
155
+ end
156
+ if pwd.to_path == '.repo'
157
+ return pwd
158
+ end
159
+ aimPath = nil
160
+ pwd.entries.each { |x|
161
+ if x.to_path == REPO_ROOT_NAME
162
+ aimPath = pwd.join(x)
163
+ break
164
+ end
165
+ }
166
+ if aimPath != nil
167
+ return aimPath
168
+ else
169
+ parentDir = pwd.dirname
170
+ if parentDir.to_path == REPO_ROOT_NAME
171
+ return nil
172
+ end
173
+ return FindRepoRoot(parentDir)
174
+ end
175
+ end
176
+ end