#!/usr/bin/env python # # Creates .wxs files to be used to generate multiple MSI targets # # by default the script will check for dist and enterprise-dist, and parse # the version as needed options are provided to give a build version that will # download the zip, drop in to dist/enterprise-dist and do the same thing # # Expected paths and names # /tmp/dist/grafana-6.0.0-ca0bc2c5pre3.windows-amd64.zip # /tmp/enterprise-dist/grafana-enterprise-6.0.0-29b28127pre3.windows-amd64.zip # # Optionally (mainly for testing), pass arguments to pull a specific build # -b,--build 5.4.3 # -e,--enterprise add this flag to specify enterprise # -p,--premium, add this flag to include premium plugins # # When using the build option, the zip file is created in either dist or # dist-enterprise according to the -e flag toggle. # # https://s3-us-west-2.amazonaws.com/grafana-releases/release/ # grafana-{}.windows-amd64.zip # # https://dl.grafana.com/enterprise/release/ # grafana-enterprise-{}.windows-amd64.zip # import os import shutil import argparse from jinja2 import Environment, FileSystemLoader from utils import * ############################# # Constants - DO NOT CHANGE # ############################# OSS_UPGRADE_VERSION = '35c7d2a9-6e23-4645-b975-e8693a1cef10' OSS_PRODUCT_NAME = 'Grafana OSS' ENTERPRISE_UPGRADE_VERSION = 'd534ec50-476b-4edc-a25e-fe854c949f4f' ENTERPRISE_PRODUCT_NAME = 'Grafana Enterprise' ############################# # CONSTANTS ############################# MSI_GENERATOR_VERSION = '1.0.0' ############################# # PATHS ############################# WIX_HOME = '/home/xclient/wix' WINE_CMD = '/usr/bin/wine64' # or just wine for 32bit CANDLE = '{} {}/candle.exe'.format(WINE_CMD, WIX_HOME) LIGHT = '{} {}/light.exe'.format(WINE_CMD, WIX_HOME) HEAT = '{} {}/heat.exe'.format(WINE_CMD, WIX_HOME) NSSM_VERSION = '2.24' DIST_LOCATION = '/tmp/dist' ############################# # ############################# grafana_oss = { 'feature_component_group_refs': [ 'GrafanaX64', 'GrafanaServiceX64', 'GrafanaFirewallExceptionsGroup' ], 'directory_refs': [ 'GrafanaX64Dir' ], 'components': [ 'grafana.wxs', 'grafana-service.wxs', 'grafana-firewall.wxs' ] } # # Grafana 6 includes new datasources with long paths # def remove_long_paths(): print('Removing long pathed files - these are not needed to run grafana') long_files = [ '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_querystring_builder.test.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_querystring_builder.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.test.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/insights_analytics/insights_analytics_datasource.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_filter_builder.test.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_filter_builder.ts', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AnalyticsConfig.test.tsx', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.test.tsx', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/InsightsConfig.test.tsx', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/AnalyticsConfig.test.tsx.snap', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/AzureCredentialsForm.test.tsx.snap', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/InsightsConfig.test.tsx.snap', '/tmp/a/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/ConfigEditor.test.tsx.snap' ] for file in long_files: if os.path.isfile(file): print('Removing: {}'.format(file)) os.remove(file) else: print('Skipped: {}'.format(file)) def build_msi(zip_file, extracted_name, PRODUCT_VERSION, grafana_hash, config, features, is_enterprise): # keep reference to source directory, will need to switch back and # forth during the process src_dir = os.getcwd() # target_dir = tempfile.TemporaryDirectory() if not os.path.isdir('/tmp/a'): os.mkdir('/tmp/a') target_dir_name = '/tmp/a' extract_zip(zip_file, target_dir_name) os.system('ls -al /tmp/a') # the zip file contains a version, which will not work when upgrading, # and ends up with paths longer # than light.exe can parse (windows issue) # Once extracted, rename it to grafana without the version included zip_file_path = '{}/{}'.format(target_dir_name, extracted_name) rename_to = '{}/grafana'.format(target_dir_name) print('Renaming extracted path {} to {}'.format(zip_file_path, rename_to)) os.system('ls -al /tmp/a') print('Before:') os.rename(zip_file_path, rename_to) print('After:') os.system('ls -al /tmp/a') # cleanup due to MSI API limitation remove_long_paths() # # HEAT # # Collects the files from the path given and generates wxs file # print('Heat Harvesting') cgname = 'GrafanaX64' cgdir = 'GrafanaX64Dir' if not os.path.isdir('/tmp/scratch'): os.mkdir('/tmp/scratch') os.chdir('/tmp/scratch') outfile = 'grafana-oss.wxs' # important flags # -srd - prevents the parent directory name from being included in the # harvest # -cg - component group to be referenced in main wxs file # -fr - directory ref to be used in main wxs file try: cmd = ''' {} dir {} \ -platform x64 \ -sw5150 \ -srd \ -cg {} \ -gg \ -sfrag \ -dr {} \ -template fragment \ -out {}'''.strip().format(HEAT, target_dir_name, cgname, cgdir, outfile) print(cmd) os.system(cmd) except Exception as ex: print(ex) shutil.copy2(outfile, target_dir_name) nssm_file = get_nssm('/tmp/cache', NSSM_VERSION) if not os.path.isdir(target_dir_name + '/nssm'): os.mkdir(target_dir_name + '/nssm') extract_zip(nssm_file, target_dir_name + '/nssm') print('HARVEST COMPLETE') os.chdir(src_dir) generate_firewall_wxs(env, PRODUCT_VERSION, '/tmp/scratch/grafana-firewall.wxs', target_dir_name) generate_service_wxs(env, PRODUCT_VERSION, '/tmp/scratch/grafana-service.wxs', target_dir_name, NSSM_VERSION) generate_product_wxs(env, config, features, '/tmp/scratch/product.wxs', target_dir_name) print('GENERATE COMPLETE') copy_static_files(target_dir_name) print('COPY STATIC COMPLETE') # # CANDLE needs to run in the scratch dir os.chdir('/tmp/scratch') try: filename = 'grafana-service.wxs' cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format(CANDLE, filename) print(cmd) os.system(cmd) shutil.copy2('grafana-service.wixobj', target_dir_name) # filename = 'grafana-firewall.wxs' cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format( CANDLE, filename) print(cmd) os.system(cmd) shutil.copy2('grafana-firewall.wixobj', target_dir_name) # filename = 'grafana-oss.wxs' cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format( CANDLE, filename) print(cmd) os.system(cmd) shutil.copy2('grafana-oss.wixobj', target_dir_name) # filename = 'product.wxs' cmd = '{} -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 {}'.format( CANDLE, filename) print(cmd) os.system(cmd) shutil.copy2('product.wixobj', target_dir_name) except Exception as ex: print(ex) print('CANDLE COMPLETE') ############################ # LIGHT - Assemble the MSI ############################ os.chdir(target_dir_name) os.system('cp -pr nssm/nssm-2.24 .') try: cmd = ''' {} \ -cultures:en-US \ -ext WixUIExtension.dll -ext WixFirewallExtension -ext WixUtilExtension \ -v -sval -spdb \ grafana-service.wixobj \ grafana-firewall.wixobj \ grafana-oss.wixobj \ product.wixobj \ -out grafana.msi'''.strip().format(LIGHT) print(cmd) os.system(cmd) except Exception as ex: print(ex) hash = '' if grafana_hash: hash = '-{}'.format(grafana_hash) # copy to scratch with version included msi_filename = '/tmp/scratch/grafana-{}{}.windows-amd64.msi'.format(PRODUCT_VERSION, hash) if is_enterprise: msi_filename = '/tmp/scratch/grafana-enterprise-{}{}.windows-amd64.msi'.format(PRODUCT_VERSION, hash) shutil.copy2('grafana.msi', msi_filename) os.system('ls -al /tmp/scratch') print('LIGHT COMPLETE') # finally cleanup # extract_dir.cleanup() def main(file_loader, env, grafana_version, grafana_hash, zip_file, extracted_name, is_enterprise): UPGRADE_VERSION = OSS_UPGRADE_VERSION GRAFANA_VERSION = grafana_version PRODUCT_TITLE = OSS_PRODUCT_NAME PRODUCT_NAME = 'GrafanaOSS' # PRODUCT_VERSION=GRAFANA_VERSION # MSI version cannot have anything other # than a x.x.x.x format, numbers only PRODUCT_VERSION = GRAFANA_VERSION.split('-')[0] LICENSE = 'LICENSE.rtf' if is_enterprise: UPGRADE_VERSION = ENTERPRISE_UPGRADE_VERSION PRODUCT_TITLE = ENTERPRISE_PRODUCT_NAME PRODUCT_NAME = 'GrafanaEnterprise' LICENSE = 'EE_LICENSE.rtf' config = { 'grafana_version': PRODUCT_VERSION, 'upgrade_code': UPGRADE_VERSION, 'product_name': PRODUCT_NAME, 'manufacturer': 'Grafana Labs', 'license': LICENSE } features = [ { 'name': PRODUCT_NAME, 'title': PRODUCT_TITLE, 'component_groups': [ { 'ref_id': 'GrafanaX64', 'directory': 'GrafanaX64Dir' } ] }, { 'name': 'GrafanaService', 'title': 'Run Grafana as a Service', 'component_groups': [ { 'ref_id': 'GrafanaServiceX64', 'directory': 'GrafanaServiceX64Dir' } ] } ] build_msi(zip_file, extracted_name, PRODUCT_VERSION, grafana_hash, config, features, is_enterprise) if __name__ == '__main__': print('MSI Generator Version: {}'.format(MSI_GENERATOR_VERSION)) parser = argparse.ArgumentParser( description='Grafana MSI Generator', formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=90, width=110), add_help=True) parser.add_argument( '-p', '--premium', help='Include premium plugins', dest='premium', action='store_true') parser.add_argument('-b', '--build', help='build to download') args = parser.parse_args() file_loader = FileSystemLoader('templates') env = Environment(loader=file_loader) grafana_version = None grafana_hash = None is_enterprise = False if not os.path.isdir(DIST_LOCATION): os.mkdir(DIST_LOCATION) # if a build version is specified, pull it if args.build: grafana_version = args.build print('Version Specified: {}'.format(grafana_version)) else: grafana_version, grafana_hash, is_enterprise = detect_version(DIST_LOCATION) print('Detected Version: {}'.format(grafana_version)) if grafana_hash: print('Detected Hash: {}'.format(grafana_hash)) print('Enterprise: {}'.format(is_enterprise)) if is_enterprise: if grafana_hash: zip_file = '{}/grafana-enterprise-{}-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version, grafana_hash) extracted_name = 'grafana-{}-{}'.format(grafana_version, grafana_hash) else: zip_file = '{}/grafana-enterprise-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version) extracted_name = 'grafana-{}'.format(grafana_version) else: # the file can have a build hash if grafana_hash: zip_file = '{}/grafana-{}-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version, grafana_hash) extracted_name = 'grafana-{}-{}'.format(grafana_version, grafana_hash) else: zip_file = '{}/grafana-{}.windows-amd64.zip'.format(DIST_LOCATION, grafana_version) extracted_name = 'grafana-{}'.format(grafana_version) print('ZipFile: {}'.format(zip_file)) # check if file downloaded if not os.path.isfile(zip_file): zip_file = get_zip(grafana_version, zip_file) main(file_loader, env, grafana_version, grafana_hash, zip_file, extracted_name, is_enterprise)