def create(unhandled_args): 'Create a new development environment' _check_working_directory_is_safe() config = build_config.load() remote = Remote(config) remote.check_version() if os.path.exists(defaults.SRC_DIR): raise ForgeError('Source folder "%s" already exists, if you really want to create a new app you will need to remove it!' % defaults.SRC_DIR) else: if "name" in forge.settings and forge.settings["name"]: name = forge.settings["name"] else: event_id = async.current_call().emit('question', schema={ 'description': 'Enter details for app', 'properties': { 'name': { 'type': 'string', 'title': 'App Name', 'description': 'This name is what your application will be called on devices. You can change it later through config.json.' } } }) name = async.current_call().wait_for_response(event_id)['data']['name'] uuid = remote.create(name) remote.fetch_initial(uuid) LOG.info("Building app for the first time...") development_build([], has_target=False) LOG.info('App structure created. To proceed:') LOG.info('1) Put your code in the "%s" folder' % defaults.SRC_DIR) LOG.info('2) Run %s build to make a build' % ENTRY_POINT_NAME) LOG.info('3) Run %s run to test out your build' % ENTRY_POINT_NAME)
def development_build(unhandled_args, has_target=True): '''Pull down new version of platform code in a customised build, and create unpacked development add-on. :param has_target: If this is False, just fetch the generation instructions, don't build any targets. ''' _check_working_directory_is_safe() if not os.path.isdir(defaults.SRC_DIR): raise ForgeError( 'Source folder "{src}" does not exist - have you run {prog} create yet?'.format( src=defaults.SRC_DIR, prog=ENTRY_POINT_NAME, ) ) config = build_config.load() remote = Remote(config) remote.check_version() manager = Manager(config) instructions_dir = defaults.INSTRUCTIONS_DIR if forge.settings.get('full', False): # do this first, so that bugs in generate_dynamic can always be nuked with a -f LOG.debug("Full rebuild requested: removing previous templates") shutil.rmtree(instructions_dir, ignore_errors=True) app_config = build_config.load_app() should_rebuild = remote.server_says_should_rebuild() server_changed = should_rebuild['should_rebuild'] reason = should_rebuild['reason'] stable_platform = should_rebuild['stable_platform'] platform_state = should_rebuild['platform_state'] if server_changed: # Need new generate dynamic - download it LOG.debug("Server requires rebuild: {reason}".format(reason=reason)) LOG.info("Your Forge platform has been updated, downloading updated build instructions.") manager.fetch_instructions() config_changed = manager.need_new_templates_for_config() if config_changed: # Need new builds due to local config change LOG.info("Your local config has been changed, downloading updated build instructions.") manager.fetch_instructions() reload_result = remote.create_buildevent(app_config) if not has_target: # No need to go further if we aren't building a target return try: target = unhandled_args.pop(0) if target.startswith("-"): raise ForgeError("Target required for 'forge build'") except IndexError: raise ForgeError("Target required for 'forge build'") # Not all targets output into a folder by the same name. target_dirs = { 'safari': 'forge.safariextension', } target_dir = target if target in target_dirs: target_dir = target_dirs[target] reload_config = json.loads(reload_result['config']) reload_config_hash = reload_result['config_hash'] if target != "reload": # Don't do a server side build for reload if not path.exists(path.join('.template', target_dir)): LOG.info("Your app configuration has changed since your last build of this platform, performing a remote build of your app. Once this is downloaded future builds will be faster.") build = remote.build(config=reload_config, target=target) remote.fetch_unpackaged(build, to_dir=defaults.TEMPLATE_DIR, target=target) else: LOG.info('Config matches previously downloaded build, performing local build.') current_platform = app_config['platform_version'] # Advise user about state of their current platform platform_category = classify_platform(stable_platform, current_platform) if platform_category == 'nonstandard': LOG.warning("Platform version: %s is a non-standard platform version, it may not be receiving updates and it is recommended you update to the stable platform version: %s" % (current_platform, stable_platform)) elif platform_category == 'minor': LOG.warning("Platform version: %s is a minor platform version, it may not be receiving updates, it is recommended you update to a major platform version" % current_platform) elif platform_category == 'old': LOG.warning("Platform version: %s is no longer the current platform version, it is recommended you migrate to a newer version using the 'forge migrate' command. See http://current-docs.trigger.io/release-notes.html for more details" % current_platform) if platform_state == "deprecated": LOG.warning("Platform version: %s is deprecated, it is highly recommended you migrate to a newer version as soon as possible." % current_platform) def move_files_across(): shutil.rmtree(path.join('development', target_dir), ignore_errors=True) if target != "reload": # Delete reload as other targets may build it shutil.rmtree(path.join('development', 'reload'), ignore_errors=True) # No reload server template shutil.copytree(path.join(defaults.TEMPLATE_DIR, target_dir), path.join('development', target_dir)) # Windows often gives a permission error without a small wait try_a_few_times(move_files_across) # Put config hash in config object for local generation # copy first as mutating dict makes assertions about previous uses tricky reload_config_for_local = reload_config.copy() reload_config_for_local['config_hash'] = reload_config_hash # have templates and instructions - inject code generator = Generate() generator.all('development', defaults.SRC_DIR, extra_args=unhandled_args, config=reload_config_for_local, target=target) LOG.info("Development build created. Use {prog} run to run your app.".format( prog=ENTRY_POINT_NAME ))