build.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. #!/usr/bin/env python
  2. #
  3. # Creates .wxs files to be used to generate multiple MSI targets
  4. #
  5. # by default the script will check for dist and enterprise-dist, and parse
  6. # the version as needed options are provided to give a build version that will
  7. # download the zip, drop in to dist/enterprise-dist and do the same thing
  8. #
  9. # Expected paths and names
  10. # /tmp/dist/grafana-6.0.0-ca0bc2c5pre3.windows-amd64.zip
  11. # /tmp/enterprise-dist/grafana-enterprise-6.0.0-29b28127pre3.windows-amd64.zip
  12. #
  13. # Optionally (mainly for testing), pass arguments to pull a specific build
  14. # -b,--build 5.4.3
  15. # -e,--enterprise add this flag to specify enterprise
  16. # -p,--premium, add this flag to include premium plugins
  17. #
  18. # When using the build option, the zip file is created in either dist or
  19. # dist-enterprise according to the -e flag toggle.
  20. #
  21. # https://s3-us-west-2.amazonaws.com/grafana-releases/release/
  22. # grafana-{}.windows-amd64.zip
  23. #
  24. # https://dl.grafana.com/enterprise/release/
  25. # grafana-enterprise-{}.windows-amd64.zip
  26. #
  27. import os
  28. import shutil
  29. import argparse
  30. from jinja2 import Template, Environment, FileSystemLoader
  31. from utils import *
  32. #############################
  33. # Constants - DO NOT CHANGE #
  34. #############################
  35. OSS_UPGRADE_VERSION = '35c7d2a9-6e23-4645-b975-e8693a1cef10'
  36. OSS_PRODUCT_NAME = 'Grafana OSS'
  37. ENTERPRISE_UPGRADE_VERSION = 'd534ec50-476b-4edc-a25e-fe854c949f4f'
  38. ENTERPRISE_PRODUCT_NAME = 'Grafana Enterprise'
  39. #############################
  40. # CONSTANTS
  41. #############################
  42. MSI_GENERATOR_VERSION = '1.0.0'
  43. #############################
  44. # PATHS
  45. #############################
  46. WIX_HOME = '/home/xclient/wix'
  47. WINE_CMD = '/usr/bin/wine64' # or just wine for 32bit
  48. CANDLE = '{} {}/candle.exe'.format(WINE_CMD, WIX_HOME)
  49. LIGHT = '{} {}/light.exe'.format(WINE_CMD, WIX_HOME)
  50. HEAT = '{} {}/heat.exe'.format(WINE_CMD, WIX_HOME)
  51. NSSM_VERSION = '2.24'
  52. DIST_LOCATION = '/tmp/dist'
  53. #############################
  54. #
  55. #############################
  56. grafana_oss = {
  57. 'feature_component_group_refs': [
  58. 'GrafanaX64',
  59. 'GrafanaServiceX64',
  60. 'GrafanaFirewallExceptionsGroup'
  61. ],
  62. 'directory_refs': [
  63. 'GrafanaX64Dir'
  64. ],
  65. 'components': [
  66. 'grafana.wxs',
  67. 'grafana-service.wxs',
  68. 'grafana-firewall.wxs'
  69. ]
  70. }
  71. #
  72. # Grafana 6 includes new datasources with long paths
  73. #
  74. def remove_long_paths():
  75. print('Removing long pathed files - these are not needed to run grafana')
  76. long_files = [
  77. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_querystring_builder.test.ts',
  78. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_querystring_builder.ts',
  79. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts',
  80. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts',
  81. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts',
  82. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts',
  83. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_filter_builder.test.ts',
  84. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_filter_builder.ts'
  85. ]
  86. for file in long_files:
  87. if os.path.isfile(file):
  88. print('Removing: {}'.format(file))
  89. os.remove(file)
  90. else:
  91. print('Skipped: {}'.format(file))
  92. def build_oss(zip_file, extracted_name, PRODUCT_VERSION, config, features):
  93. # keep reference to source directory, will need to switch back and
  94. # forth during the process
  95. src_dir = os.getcwd()
  96. # target_dir = tempfile.TemporaryDirectory()
  97. if not os.path.isdir('/tmp/a'):
  98. os.mkdir('/tmp/a')
  99. target_dir_name = '/tmp/a'
  100. extract_zip(zip_file, target_dir_name)
  101. # the zip file contains a version, which will not work when upgrading,
  102. # and ends up with paths longer
  103. # than light.exe can parse (windows issue)
  104. # Once extracted, rename it to grafana without the version included
  105. zip_file_path = '{}/{}'.format(target_dir_name, extracted_name)
  106. rename_to = '{}/grafana'.format(target_dir_name)
  107. print('Renaming extracted path {} to {}'.format(zip_file_path, rename_to))
  108. os.system('ls -al /tmp/a')
  109. print('Before:')
  110. os.rename(zip_file_path, rename_to)
  111. print('After:')
  112. os.system('ls -al /tmp/a')
  113. # cleanup due to MSI API limitation
  114. remove_long_paths()
  115. #
  116. # HEAT
  117. #
  118. # Collects the files from the path given and generates wxs file
  119. #
  120. print('Heat Harvesting')
  121. cgname = 'GrafanaX64'
  122. cgdir = 'GrafanaX64Dir'
  123. if not os.path.isdir('/tmp/scratch'):
  124. os.mkdir('/tmp/scratch')
  125. os.chdir('/tmp/scratch')
  126. outfile = 'grafana-oss.wxs'
  127. # important flags
  128. # -srd - prevents the parent directory name from being included in the
  129. # harvest
  130. # -cg - component group to be referenced in main wxs file
  131. # -fr - directory ref to be used in main wxs file
  132. try:
  133. cmd = '''
  134. {} dir {} \
  135. -platform x64 \
  136. -sw5150 \
  137. -srd \
  138. -cg {} \
  139. -gg \
  140. -sfrag \
  141. -dr {} \
  142. -template fragment \
  143. -out {}'''.strip().format(HEAT, target_dir_name, cgname, cgdir, outfile)
  144. print(cmd)
  145. os.system(cmd)
  146. except Exception as ex:
  147. print(ex)
  148. shutil.copy2(outfile, target_dir_name)
  149. nssm_file = get_nssm('/tmp/cache', NSSM_VERSION)
  150. if not os.path.isdir(target_dir_name + '/nssm'):
  151. os.mkdir(target_dir_name + '/nssm')
  152. extract_zip(nssm_file, target_dir_name + '/nssm')
  153. print('HARVEST COMPLETE')
  154. os.chdir(src_dir)
  155. generate_firewall_wxs(env, PRODUCT_VERSION, '/tmp/scratch/grafana-firewall.wxs', target_dir_name)
  156. generate_service_wxs(env, PRODUCT_VERSION, '/tmp/scratch/grafana-service.wxs', target_dir_name, NSSM_VERSION)
  157. generate_product_wxs(env, config, features, '/tmp/scratch/product.wxs', target_dir_name)
  158. print('GENERATE COMPLETE')
  159. copy_static_files(target_dir_name)
  160. print('COPY STATIC COMPLETE')
  161. #
  162. # CANDLE needs to run in the scratch dir
  163. os.chdir('/tmp/scratch')
  164. try:
  165. filename = 'grafana-service.wxs'
  166. cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format(CANDLE, filename)
  167. print(cmd)
  168. os.system(cmd)
  169. shutil.copy2('grafana-service.wixobj', target_dir_name)
  170. #
  171. filename = 'grafana-firewall.wxs'
  172. cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format(
  173. CANDLE,
  174. filename)
  175. print(cmd)
  176. os.system(cmd)
  177. shutil.copy2('grafana-firewall.wixobj', target_dir_name)
  178. #
  179. filename = 'grafana-oss.wxs'
  180. cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format(
  181. CANDLE,
  182. filename)
  183. print(cmd)
  184. os.system(cmd)
  185. shutil.copy2('grafana-oss.wixobj', target_dir_name)
  186. #
  187. filename = 'product.wxs'
  188. cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format(
  189. CANDLE,
  190. filename)
  191. print(cmd)
  192. os.system(cmd)
  193. shutil.copy2('product.wixobj', target_dir_name)
  194. except Exception as ex:
  195. print(ex)
  196. print('CANDLE COMPLETE')
  197. ############################
  198. # LIGHT - Assemble the MSI
  199. ############################
  200. os.chdir(target_dir_name)
  201. os.system('cp -pr nssm/nssm-2.24 .')
  202. try:
  203. cmd = '''
  204. {} \
  205. -cultures:en-US \
  206. -ext WixUIExtension.dll -ext WixFirewallExtension -ext WixUtilExtension \
  207. -v -sval -spdb \
  208. grafana-service.wixobj \
  209. grafana-firewall.wixobj \
  210. grafana-oss.wixobj \
  211. product.wixobj \
  212. -out grafana.msi'''.strip().format(LIGHT)
  213. print(cmd)
  214. os.system(cmd)
  215. except Exception as ex:
  216. print(ex)
  217. # copy to scratch with version included
  218. msi_filename = '/tmp/scratch/{}.windows-amd64.msi'.format(extracted_name)
  219. shutil.copy2('grafana.msi', msi_filename)
  220. os.system('ls -al /tmp/scratch')
  221. print('LIGHT COMPLETE')
  222. # finally cleanup
  223. # extract_dir.cleanup()
  224. def main(file_loader, env, grafana_version, zip_file, extracted_name):
  225. UPGRADE_VERSION = OSS_UPGRADE_VERSION
  226. GRAFANA_VERSION = grafana_version
  227. PRODUCT_NAME = OSS_PRODUCT_NAME
  228. # PRODUCT_VERSION=GRAFANA_VERSION
  229. # MSI version cannot have anything other
  230. # than a x.x.x.x format, numbers only
  231. PRODUCT_VERSION = GRAFANA_VERSION.split('-')[0]
  232. config = {
  233. 'grafana_version': PRODUCT_VERSION,
  234. 'upgrade_code': UPGRADE_VERSION,
  235. 'product_name': PRODUCT_NAME,
  236. 'manufacturer': 'Grafana Labs'
  237. }
  238. features = [
  239. {
  240. 'name': 'GrafanaOSS',
  241. 'title': PRODUCT_NAME,
  242. 'component_groups': [
  243. {
  244. 'ref_id': 'GrafanaX64',
  245. 'directory': 'GrafanaX64Dir'
  246. }
  247. ]
  248. },
  249. {
  250. 'name': 'GrafanaService',
  251. 'title': 'Run Grafana as a Service',
  252. 'component_groups': [
  253. {
  254. 'ref_id': 'GrafanaServiceX64',
  255. 'directory': 'GrafanaServiceX64Dir'
  256. }
  257. ]
  258. }
  259. ]
  260. build_oss(zip_file, extracted_name, PRODUCT_VERSION, config, features)
  261. if __name__ == '__main__':
  262. print('MSI Generator Version: {}'.format(MSI_GENERATOR_VERSION))
  263. parser = argparse.ArgumentParser(
  264. description='Grafana MSI Generator',
  265. formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=90, width=110), add_help=True)
  266. parser.add_argument(
  267. '-p',
  268. '--premium',
  269. help='Include premium plugins',
  270. dest='premium', action='store_true')
  271. parser.add_argument(
  272. '-e',
  273. '--enterprise',
  274. help='Use Enterprise build',
  275. dest='enterprise',
  276. action='store_true')
  277. parser.set_defaults(enterprise=False, premium=False)
  278. parser.add_argument('-b', '--build', help='build to download')
  279. args = parser.parse_args()
  280. file_loader = FileSystemLoader('templates')
  281. env = Environment(loader=file_loader)
  282. grafana_version = None
  283. grafana_hash = None
  284. is_enterprise = False
  285. if not os.path.isdir(DIST_LOCATION):
  286. os.mkdir(DIST_LOCATION)
  287. # if a build version is specified, pull it
  288. if args.build:
  289. grafana_version = args.build
  290. print('Version Specified: {}'.format(grafana_version))
  291. else:
  292. grafana_version, grafana_hash, is_enterprise = detect_version(DIST_LOCATION)
  293. # check for enterprise flag
  294. if args.enterprise:
  295. grafana_version = 'enterprise-{}'.format(args.build)
  296. #
  297. print('Detected Version: {}'.format(grafana_version))
  298. if grafana_hash:
  299. print('Detected Hash: {}'.format(grafana_hash))
  300. print('Enterprise: {}'.format(is_enterprise))
  301. if is_enterprise:
  302. zip_file = '{}/grafana-enterprise-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version)
  303. extracted_name = 'grafana-enterprise-{}'.format(grafana_version)
  304. else:
  305. # the file can have a build hash
  306. if grafana_hash:
  307. zip_file = '{}/grafana-{}-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version, grafana_hash)
  308. extracted_name = 'grafana-{}-{}'.format(grafana_version, grafana_hash)
  309. else:
  310. zip_file = '{}/grafana-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version)
  311. extracted_name = 'grafana-{}'.format(grafana_version)
  312. print('ZipFile: {}'.format(zip_file))
  313. # check if file downloaded
  314. if not os.path.isfile(zip_file):
  315. zip_file = get_zip(grafana_version, zip_file)
  316. main(file_loader, env, grafana_version, zip_file, extracted_name)