build.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AnalyticsConfig.test.tsx',
  86. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.test.tsx',
  87. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/InsightsConfig.test.tsx',
  88. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/AnalyticsConfig.test.tsx.snap',
  89. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/AzureCredentialsForm.test.tsx.snap',
  90. '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/InsightsConfig.test.tsx.snap'
  91. ]
  92. for file in long_files:
  93. if os.path.isfile(file):
  94. print('Removing: {}'.format(file))
  95. os.remove(file)
  96. else:
  97. print('Skipped: {}'.format(file))
  98. def build_oss(zip_file, extracted_name, PRODUCT_VERSION, config, features):
  99. # keep reference to source directory, will need to switch back and
  100. # forth during the process
  101. src_dir = os.getcwd()
  102. # target_dir = tempfile.TemporaryDirectory()
  103. if not os.path.isdir('/tmp/a'):
  104. os.mkdir('/tmp/a')
  105. target_dir_name = '/tmp/a'
  106. extract_zip(zip_file, target_dir_name)
  107. # the zip file contains a version, which will not work when upgrading,
  108. # and ends up with paths longer
  109. # than light.exe can parse (windows issue)
  110. # Once extracted, rename it to grafana without the version included
  111. zip_file_path = '{}/{}'.format(target_dir_name, extracted_name)
  112. rename_to = '{}/grafana'.format(target_dir_name)
  113. print('Renaming extracted path {} to {}'.format(zip_file_path, rename_to))
  114. os.system('ls -al /tmp/a')
  115. print('Before:')
  116. os.rename(zip_file_path, rename_to)
  117. print('After:')
  118. os.system('ls -al /tmp/a')
  119. # cleanup due to MSI API limitation
  120. remove_long_paths()
  121. #
  122. # HEAT
  123. #
  124. # Collects the files from the path given and generates wxs file
  125. #
  126. print('Heat Harvesting')
  127. cgname = 'GrafanaX64'
  128. cgdir = 'GrafanaX64Dir'
  129. if not os.path.isdir('/tmp/scratch'):
  130. os.mkdir('/tmp/scratch')
  131. os.chdir('/tmp/scratch')
  132. outfile = 'grafana-oss.wxs'
  133. # important flags
  134. # -srd - prevents the parent directory name from being included in the
  135. # harvest
  136. # -cg - component group to be referenced in main wxs file
  137. # -fr - directory ref to be used in main wxs file
  138. try:
  139. cmd = '''
  140. {} dir {} \
  141. -platform x64 \
  142. -sw5150 \
  143. -srd \
  144. -cg {} \
  145. -gg \
  146. -sfrag \
  147. -dr {} \
  148. -template fragment \
  149. -out {}'''.strip().format(HEAT, target_dir_name, cgname, cgdir, outfile)
  150. print(cmd)
  151. os.system(cmd)
  152. except Exception as ex:
  153. print(ex)
  154. shutil.copy2(outfile, target_dir_name)
  155. nssm_file = get_nssm('/tmp/cache', NSSM_VERSION)
  156. if not os.path.isdir(target_dir_name + '/nssm'):
  157. os.mkdir(target_dir_name + '/nssm')
  158. extract_zip(nssm_file, target_dir_name + '/nssm')
  159. print('HARVEST COMPLETE')
  160. os.chdir(src_dir)
  161. generate_firewall_wxs(env, PRODUCT_VERSION, '/tmp/scratch/grafana-firewall.wxs', target_dir_name)
  162. generate_service_wxs(env, PRODUCT_VERSION, '/tmp/scratch/grafana-service.wxs', target_dir_name, NSSM_VERSION)
  163. generate_product_wxs(env, config, features, '/tmp/scratch/product.wxs', target_dir_name)
  164. print('GENERATE COMPLETE')
  165. copy_static_files(target_dir_name)
  166. print('COPY STATIC COMPLETE')
  167. #
  168. # CANDLE needs to run in the scratch dir
  169. os.chdir('/tmp/scratch')
  170. try:
  171. filename = 'grafana-service.wxs'
  172. cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format(CANDLE, filename)
  173. print(cmd)
  174. os.system(cmd)
  175. shutil.copy2('grafana-service.wixobj', target_dir_name)
  176. #
  177. filename = 'grafana-firewall.wxs'
  178. cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format(
  179. CANDLE,
  180. filename)
  181. print(cmd)
  182. os.system(cmd)
  183. shutil.copy2('grafana-firewall.wixobj', target_dir_name)
  184. #
  185. filename = 'grafana-oss.wxs'
  186. cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format(
  187. CANDLE,
  188. filename)
  189. print(cmd)
  190. os.system(cmd)
  191. shutil.copy2('grafana-oss.wixobj', target_dir_name)
  192. #
  193. filename = 'product.wxs'
  194. cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format(
  195. CANDLE,
  196. filename)
  197. print(cmd)
  198. os.system(cmd)
  199. shutil.copy2('product.wixobj', target_dir_name)
  200. except Exception as ex:
  201. print(ex)
  202. print('CANDLE COMPLETE')
  203. ############################
  204. # LIGHT - Assemble the MSI
  205. ############################
  206. os.chdir(target_dir_name)
  207. os.system('cp -pr nssm/nssm-2.24 .')
  208. try:
  209. cmd = '''
  210. {} \
  211. -cultures:en-US \
  212. -ext WixUIExtension.dll -ext WixFirewallExtension -ext WixUtilExtension \
  213. -v -sval -spdb \
  214. grafana-service.wixobj \
  215. grafana-firewall.wixobj \
  216. grafana-oss.wixobj \
  217. product.wixobj \
  218. -out grafana.msi'''.strip().format(LIGHT)
  219. print(cmd)
  220. os.system(cmd)
  221. except Exception as ex:
  222. print(ex)
  223. # copy to scratch with version included
  224. msi_filename = '/tmp/scratch/{}.windows-amd64.msi'.format(extracted_name)
  225. shutil.copy2('grafana.msi', msi_filename)
  226. os.system('ls -al /tmp/scratch')
  227. print('LIGHT COMPLETE')
  228. # finally cleanup
  229. # extract_dir.cleanup()
  230. def main(file_loader, env, grafana_version, zip_file, extracted_name):
  231. UPGRADE_VERSION = OSS_UPGRADE_VERSION
  232. GRAFANA_VERSION = grafana_version
  233. PRODUCT_NAME = OSS_PRODUCT_NAME
  234. # PRODUCT_VERSION=GRAFANA_VERSION
  235. # MSI version cannot have anything other
  236. # than a x.x.x.x format, numbers only
  237. PRODUCT_VERSION = GRAFANA_VERSION.split('-')[0]
  238. config = {
  239. 'grafana_version': PRODUCT_VERSION,
  240. 'upgrade_code': UPGRADE_VERSION,
  241. 'product_name': PRODUCT_NAME,
  242. 'manufacturer': 'Grafana Labs'
  243. }
  244. features = [
  245. {
  246. 'name': 'GrafanaOSS',
  247. 'title': PRODUCT_NAME,
  248. 'component_groups': [
  249. {
  250. 'ref_id': 'GrafanaX64',
  251. 'directory': 'GrafanaX64Dir'
  252. }
  253. ]
  254. },
  255. {
  256. 'name': 'GrafanaService',
  257. 'title': 'Run Grafana as a Service',
  258. 'component_groups': [
  259. {
  260. 'ref_id': 'GrafanaServiceX64',
  261. 'directory': 'GrafanaServiceX64Dir'
  262. }
  263. ]
  264. }
  265. ]
  266. build_oss(zip_file, extracted_name, PRODUCT_VERSION, config, features)
  267. if __name__ == '__main__':
  268. print('MSI Generator Version: {}'.format(MSI_GENERATOR_VERSION))
  269. parser = argparse.ArgumentParser(
  270. description='Grafana MSI Generator',
  271. formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=90, width=110), add_help=True)
  272. parser.add_argument(
  273. '-p',
  274. '--premium',
  275. help='Include premium plugins',
  276. dest='premium', action='store_true')
  277. parser.add_argument(
  278. '-e',
  279. '--enterprise',
  280. help='Use Enterprise build',
  281. dest='enterprise',
  282. action='store_true')
  283. parser.set_defaults(enterprise=False, premium=False)
  284. parser.add_argument('-b', '--build', help='build to download')
  285. args = parser.parse_args()
  286. file_loader = FileSystemLoader('templates')
  287. env = Environment(loader=file_loader)
  288. grafana_version = None
  289. grafana_hash = None
  290. is_enterprise = False
  291. if not os.path.isdir(DIST_LOCATION):
  292. os.mkdir(DIST_LOCATION)
  293. # if a build version is specified, pull it
  294. if args.build:
  295. grafana_version = args.build
  296. print('Version Specified: {}'.format(grafana_version))
  297. else:
  298. grafana_version, grafana_hash, is_enterprise = detect_version(DIST_LOCATION)
  299. # check for enterprise flag
  300. if args.enterprise:
  301. grafana_version = 'enterprise-{}'.format(args.build)
  302. #
  303. print('Detected Version: {}'.format(grafana_version))
  304. if grafana_hash:
  305. print('Detected Hash: {}'.format(grafana_hash))
  306. print('Enterprise: {}'.format(is_enterprise))
  307. if is_enterprise:
  308. zip_file = '{}/grafana-enterprise-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version)
  309. extracted_name = 'grafana-enterprise-{}'.format(grafana_version)
  310. else:
  311. # the file can have a build hash
  312. if grafana_hash:
  313. zip_file = '{}/grafana-{}-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version, grafana_hash)
  314. extracted_name = 'grafana-{}-{}'.format(grafana_version, grafana_hash)
  315. else:
  316. zip_file = '{}/grafana-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version)
  317. extracted_name = 'grafana-{}'.format(grafana_version)
  318. print('ZipFile: {}'.format(zip_file))
  319. # check if file downloaded
  320. if not os.path.isfile(zip_file):
  321. zip_file = get_zip(grafana_version, zip_file)
  322. main(file_loader, env, grafana_version, zip_file, extracted_name)