예제 #1
0
class TestFetchTemplateAppsAndInstructions(object):
	def setup(self):
		self.manager = Manager(dummy_config())
		
	@mock.patch('forge.templates.shutil')
	@mock.patch('forge.templates.Remote')
	@mock.patch('forge.templates.tempfile')
	@mock.patch('forge.templates.import_generate_dynamic')
	def test_no_templates(self, import_generate_dynamic, tempfile, Remote, shutil):
		remote = Remote.return_value
		temp_dir = tempfile.mkdtemp.return_value = 'dummy temp dir'
		
		self.manager.fetch_template_apps_and_instructions(-1)
		
		temp_templates_dir = path.join(temp_dir, '.template')
		temp_instructions_dir = path.join(temp_dir, '.template')

		remote.fetch_unpackaged.assert_called_once_with(-1, to_dir=temp_templates_dir)
		remote.fetch_generate_instructions.assert_called_once_with(temp_instructions_dir)
		
		shutil.rmtree.assert_has_calls(
			[mock.call('.template', ignore_errors=True), mock.call(temp_dir, ignore_errors=True)]
		)

		shutil.move.assert_called_once_with(temp_templates_dir, '.template')
		# ensure invalidation of any cached generate_dynamic module
		import_generate_dynamic.assert_called_once_with(do_reload=True)
예제 #2
0
class TestNeedNewTemplatesFor(object):
	def setup(self):
		self.manager = Manager(dummy_config())
		
	@mock.patch('forge.templates.path')
	@mock.patch('forge.templates.import_generate_dynamic')
	def test_no_changes(self, import_generate_dynamic, path):
		internal_goals = import_generate_dynamic.return_value.internal_goals
		internal_goals.config_changes_invalidate_templates.return_value = False
		path.isdir.return_value = True

		res = self.manager.need_new_templates_for_config()

		eq_(res, False)

	@mock.patch('forge.templates.path')
	@mock.patch('forge.templates.import_generate_dynamic')
	def test_important_changes(self, import_generate_dynamic, path):
		internal_goals = import_generate_dynamic.return_value.internal_goals
		internal_goals.config_changes_invalidate_templates.return_value = True
		path.isdir.return_value = True
		
		res = self.manager.need_new_templates_for_config()
		
		eq_(res, True)

	@mock.patch('forge.templates.path')
	def test_dir_not_there(self, path):
		path.isdir.return_value = False
		
		res = self.manager.need_new_templates_for_config()
		
		eq_(res, True)

	@mock.patch('forge.templates.path')
	def test_old_config_not_there(self, path):
		path.isfile.return_value = False
		
		res = self.manager.need_new_templates_for_config()
		
		eq_(res, True)
예제 #3
0
	def setup(self):
		self.manager = Manager(dummy_config())
예제 #4
0
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
	))