Esempio n. 1
0
def check_merge(master, new, expected):
	master_doc = minidom.parseString(header + master + footer)
	new_doc = minidom.parseString(header + new + footer)
	merge.merge(master_doc, new_doc)

	expected_doc = minidom.parseString(header + expected + footer)

	def remove_boring(doc):
		for node in list(doc.documentElement.childNodes):
			if node.localName in ('name', 'summary', 'description'):
				doc.documentElement.removeChild(node)
	remove_boring(master_doc)
	remove_boring(expected_doc)

	formatting.format_node(master_doc.documentElement, "\n")
	formatting.format_node(expected_doc.documentElement, "\n")

	master_doc.normalize()
	expected_doc.normalize()

	if xmltools.nodes_equal(master_doc.documentElement, expected_doc.documentElement):
		return

	actual = master_doc.documentElement.toxml()
	expected = expected_doc.documentElement.toxml()

	assert actual != expected

	raise Exception("Failed.\n\nExpected:\n{}\nActual:\n{}".format(expected, actual))
Esempio n. 2
0
def check_merge(master, new, expected):
    master_doc = minidom.parseString(header + master + footer)
    new_doc = minidom.parseString(header + new + footer)
    merge.merge(master_doc, new_doc)

    expected_doc = minidom.parseString(header + expected + footer)

    def remove_boring(doc):
        for node in list(doc.documentElement.childNodes):
            if node.localName in ('name', 'summary', 'description'):
                doc.documentElement.removeChild(node)

    remove_boring(master_doc)
    remove_boring(expected_doc)

    formatting.format_node(master_doc.documentElement, "\n")
    formatting.format_node(expected_doc.documentElement, "\n")

    master_doc.normalize()
    expected_doc.normalize()

    if xmltools.nodes_equal(master_doc.documentElement,
                            expected_doc.documentElement):
        return

    actual = master_doc.documentElement.toxml()
    expected = expected_doc.documentElement.toxml()

    assert actual != expected

    raise Exception("Failed.\n\nExpected:\n{}\nActual:\n{}".format(
        expected, actual))
Esempio n. 3
0
def build_public_feeds(config):
	feeds = []
	for dirpath, dirnames, filenames in os.walk('feeds'):
		for f in filenames:
			if f.endswith('.xml') and not f.startswith('.'):
				source_path = join(dirpath, f)
				public_rel_path = paths.get_public_rel_path(config, relpath(source_path, 'feeds'))
				target_path = join("public", public_rel_path)
				new_doc = generate_public_xml(config, source_path)
				changed = True
				if os.path.exists(target_path):
					with open(target_path, 'rb') as stream:
						old_doc = minidom.parse(stream)
					if xmltools.nodes_equal(old_doc.documentElement, new_doc.documentElement):
						#print("%s unchanged" % source_path)
						changed = False
				feeds.append(PublicFeed(abspath(source_path), public_rel_path, new_doc, changed))

	if config.GPG_SIGNING_KEY:
		key_path = export_key(join('public', 'keys'), config.GPG_SIGNING_KEY)
		other_files = [relpath(key_path, 'public')]
	else:
		other_files = []

	for public_feed in feeds:
		target_path = join('public', public_feed.public_rel_path)

		target_dir = dirname(target_path)
		if not os.path.isdir(target_dir):
			os.makedirs(target_dir)

		if config.GPG_SIGNING_KEY and config.GPG_PUBLIC_KEY_DIRECTORY:
			key_symlink_rel_path = join(dirname(public_feed.public_rel_path), config.GPG_PUBLIC_KEY_DIRECTORY, basename(key_path))
			other_files.append(key_symlink_rel_path)
			key_symlink_path = join('public', key_symlink_rel_path)
			if not os.path.exists(key_symlink_path):
				if os.name == 'nt':
					import shutil
					shutil.copyfile(key_path, key_symlink_path)
				else:
					os.symlink(relpath(key_path, dirname(key_symlink_path)), key_symlink_path)
			os.stat(key_symlink_path)

		if not public_feed.changed: continue

		path_to_resources = relpath(join('public', 'resources'), dirname(target_path))
		new_xml = (feed_header % path_to_resources).encode('utf-8') + public_feed.doc.documentElement.toxml('utf-8') + '\n'

		signed_xml = sign_xml(config, new_xml)

		with open(target_path + '.new', 'wb') as stream:
			stream.write(signed_xml)
		support.portable_rename(target_path + '.new', target_path)
		print("Updated", target_path)

	return feeds, other_files
Esempio n. 4
0
def build_public_feeds(config):
	feeds = []
	for dirpath, dirnames, filenames in os.walk('feeds'):
		for f in filenames:
			if f.endswith('.xml') and not f.startswith('.'):
				source_path = join(dirpath, f)
				public_rel_path = paths.get_public_rel_path(config, relpath(source_path, 'feeds'))
				target_path = join("public", public_rel_path)
				new_doc = generate_public_xml(config, source_path)
				changed = True
				if os.path.exists(target_path):
					with open(target_path, 'rb') as stream:
						old_doc = minidom.parse(stream)
					if xmltools.nodes_equal(old_doc.documentElement, new_doc.documentElement):
						#print("%s unchanged" % source_path)
						changed = False
				feeds.append(PublicFeed(abspath(source_path), public_rel_path, new_doc, changed))

	key_path = export_key(join('public', 'keys'), config.GPG_SIGNING_KEY)

	other_files = [relpath(key_path, 'public')]

	for public_feed in feeds:
		target_path = join('public', public_feed.public_rel_path)

		target_dir = dirname(target_path)
		if not os.path.isdir(target_dir):
			os.makedirs(target_dir)

		if config.GPG_PUBLIC_KEY_DIRECTORY:
			key_symlink_rel_path = join(dirname(public_feed.public_rel_path), config.GPG_PUBLIC_KEY_DIRECTORY, basename(key_path))
			other_files.append(key_symlink_rel_path)
			key_symlink_path = join('public', key_symlink_rel_path)
			if not os.path.exists(key_symlink_path):
				if os.name == 'nt':
					import shutil
					shutil.copyfile(key_path, key_symlink_path)
				else:
					os.symlink(relpath(key_path, dirname(key_symlink_path)), key_symlink_path)
			os.stat(key_symlink_path)

		if not public_feed.changed: continue

		path_to_resources = relpath(join('public', 'resources'), dirname(target_path))
		new_xml = (feed_header % path_to_resources).encode('utf-8') + public_feed.doc.documentElement.toxml('utf-8') + '\n'

		signed_xml = sign_xml(config, new_xml)

		with open(target_path + '.new', 'wb') as stream:
			stream.write(signed_xml)
		support.portable_rename(target_path + '.new', target_path)
		print("Updated", target_path)

	return feeds, other_files
Esempio n. 5
0
def write_catalog(config, feeds, dir_rel_path):
	cat_ns = namespace.Namespace()
	cat_ns.register_namespace(XMLNS_CATALOG, "c")

	impl = minidom.getDOMImplementation()
	cat_doc = impl.createDocument(XMLNS_CATALOG, "c:catalog", None)
	cat_root = cat_doc.documentElement
	cat_root.setAttributeNS(XMLNS_NAMESPACE, 'xmlns:c', XMLNS_CATALOG)
	cat_root.setAttributeNS(XMLNS_NAMESPACE, 'xmlns', XMLNS_IFACE)

	custom_tags = {}
	for (name, ns, tags) in getattr(config, 'ADDITIONAL_CATALOG_TAGS', []):
		cat_ns.register_namespace(ns, name)
		cat_root.setAttributeNS(XMLNS_NAMESPACE, 'xmlns:' + name, ns)
		custom_tags[ns] = tags

	feed_roots = [feed.doc.documentElement for feed in feeds]

	def get_name(feed_root):
		return feed_root.getElementsByTagName('name')[0].firstChild.wholeText

	is_excluded_from_catalog = getattr(config, 'is_excluded_from_catalog', _default_is_excluded_from_catalog)

	for feed_root in sorted(feed_roots, key=get_name):
		if is_excluded_from_catalog(feed_root, dir_rel_path): continue
		elem = cat_doc.createElementNS(XMLNS_IFACE, "interface")
		elem.setAttribute('uri', feed_root.getAttribute("uri"))
		for feed_elem in feed_root.childNodes:
			ns = feed_elem.namespaceURI
			if ((ns == XMLNS_IFACE and feed_elem.localName in catalog_names) or
				(ns in custom_tags and feed_elem.localName in custom_tags[ns])):
				elem.appendChild(cat_ns.import_node(cat_doc, feed_elem))
		cat_root.appendChild(elem)

	catalog_file = join('public', dir_rel_path, 'catalog.xml')

	need_update = True
	if os.path.exists(catalog_file):
		with open(catalog_file, 'rb') as stream:
			old_catalog = minidom.parse(stream)
		need_update = not xmltools.nodes_equal(old_catalog.documentElement, cat_doc.documentElement)

	if need_update:
		path_to_resources = relpath('resources', dir_rel_path).replace(os.sep, '/').encode()
		new_data = build.sign_xml(config, (catalog_header % path_to_resources) + cat_doc.documentElement.toxml(encoding = 'utf-8') + b'\n')
		with open(catalog_file + '.new', 'wb') as stream:
			stream.write(new_data)
		support.portable_rename(catalog_file + '.new', catalog_file)
		print("Updated " + catalog_file)

	return join(dir_rel_path, 'catalog.xml')
Esempio n. 6
0
def write_catalog(config, feeds):
	cat_ns = namespace.Namespace()
	cat_ns.register_namespace(XMLNS_CATALOG, "c")

	impl = minidom.getDOMImplementation()
	cat_doc = impl.createDocument(XMLNS_CATALOG, "c:catalog", None)
	cat_root = cat_doc.documentElement
	cat_root.setAttributeNS(XMLNS_NAMESPACE, 'xmlns:c', XMLNS_CATALOG)
	cat_root.setAttributeNS(XMLNS_NAMESPACE, 'xmlns', XMLNS_IFACE)

	custom_tags = {}
	for (name, ns, tags) in getattr(config, 'ADDITIONAL_CATALOG_TAGS', []):
		cat_ns.register_namespace(ns, name)
		cat_root.setAttributeNS(XMLNS_NAMESPACE, 'xmlns:' + name, ns)
		custom_tags[ns] = tags

	feed_roots = [feed.doc.documentElement for feed in feeds]

	def get_name(feed_root):
		return feed_root.getElementsByTagName('name')[0].firstChild.wholeText

	def is_replaced(feed_root):
		return feed_root.getElementsByTagName('replaced-by').length > 0

	for feed_root in sorted(feed_roots, key=get_name):
		if is_replaced(feed_root): continue
		elem = cat_doc.createElementNS(XMLNS_IFACE, "interface")
		elem.setAttribute('uri', feed_root.getAttribute("uri"))
		for feed_elem in feed_root.childNodes:
			ns = feed_elem.namespaceURI
			if ((ns == XMLNS_IFACE and feed_elem.localName in catalog_names) or
				(ns in custom_tags and feed_elem.localName in custom_tags[ns])):
				elem.appendChild(cat_ns.import_node(cat_doc, feed_elem))
		cat_root.appendChild(elem)
	
	catalog_file = join('public', 'catalog.xml')

	need_update = True
	if os.path.exists(catalog_file):
		with open(catalog_file, 'rb') as stream:
			old_catalog = minidom.parse(stream)
		need_update = not xmltools.nodes_equal(old_catalog.documentElement, cat_doc.documentElement)

	if need_update:
		new_data = build.sign_xml(config, catalog_header + cat_doc.documentElement.toxml(encoding = 'utf-8') + '\n')
		with open(catalog_file + '.new', 'wb') as stream:
			stream.write(new_data)
		support.portable_rename(catalog_file + '.new', catalog_file)
		print("Updated catalog.xml")

	return ['catalog.xml']
Esempio n. 7
0
def handle(config, options, args):
    if len(args) != 1:
        raise UsageError()

    assert not options.offline

    old_gui = options.gui

    app = config.app_mgr.lookup_app(args[0], missing_ok=True)
    if app is not None:
        old_sels = app.get_selections()
        old_selections = old_sels.selections
        iface_uri = old_sels.interface
        r = app.get_requirements()
        r.parse_update_options(options)
    else:
        iface_uri = model.canonical_iface_uri(args[0])

        r = requirements.Requirements(iface_uri)
        r.parse_options(options)

        # Select once in offline console mode to get the old values
        options.offline = True
        options.gui = False
        options.refresh = False

        try:
            old_sels = select.get_selections_for(r,
                                                 config,
                                                 options,
                                                 select_only=True,
                                                 download_only=False,
                                                 test_callback=None)
        except SafeException:
            old_selections = {}
        else:
            if old_sels is None:
                old_selections = {}
            else:
                old_selections = old_sels.selections

    # Download in online mode to get the new values
    config.network_use = model.network_full
    options.offline = False
    options.gui = old_gui
    options.refresh = True

    sels = select.get_selections_for(r,
                                     config,
                                     options,
                                     select_only=False,
                                     download_only=True,
                                     test_callback=None)
    if not sels:
        sys.exit(1)  # Aborted by user

    root_feed = config.iface_cache.get_feed(iface_uri)
    if root_feed:
        target = root_feed.get_replaced_by()
        if target is not None:
            print(
                _("Warning: interface {old} has been replaced by {new}".format(
                    old=iface_uri, new=target)))

    from zeroinstall.cmd import whatchanged
    changes = whatchanged.show_changes(old_selections, sels.selections)
    root_sel = sels[iface_uri]

    if not changes:
        from zeroinstall.support import xmltools
        # No obvious changes, but check for more subtle updates.
        if not xmltools.nodes_equal(sels.toDOM(), old_sels.toDOM()):
            changes = True
            print(
                _("Updates to metadata found, but no change to version ({version})."
                  ).format(version=root_sel.version))

    root_iface = config.iface_cache.get_interface(iface_uri)
    # Force a reload, since we may have used the GUI to update it
    for feed in config.iface_cache.get_feeds(root_iface):
        config.iface_cache.get_feed(feed, force=True)

    root_impls = config.iface_cache.get_implementations(root_iface)

    latest = max((impl.version, impl) for impl in root_impls)[1]
    if latest.version > model.parse_version(sels[iface_uri].version):
        print(
            _("A later version ({name} {latest}) exists but was not selected. Using {version} instead."
              ).format(latest=latest.get_version(),
                       name=root_iface.get_name(),
                       version=root_sel.version))
        if not config.help_with_testing and latest.get_stability(
        ) < model.stable:
            print(
                _('To select "testing" versions, use:\n0install config help_with_testing True'
                  ))
    elif not changes:
        print(
            _("No updates found. Continuing with version {version}.").format(
                version=root_sel.version))

    if app is not None:
        if changes:
            app.set_selections(sels)
        app.set_requirements(r)
Esempio n. 8
0
	def _check_for_updates(self, sels, use_gui):
		"""Check whether the selections need to be updated.
		If any input feeds have changed, we re-run the solver. If the
		new selections require a download, we schedule one in the
		background and return the old selections. Otherwise, we return the
		new selections. If we can select better versions without downloading,
		we update the app's selections and return the new selections.
		If we can't use the current selections, we update in the foreground.
		We also schedule a background update from time-to-time anyway.
		@type sels: L{zeroinstall.injector.selections.Selections}
		@type use_gui: bool
		@return: the selections to use
		@rtype: L{selections.Selections}"""
		need_solve = False		# Rerun solver (cached feeds have changed)
		need_update = False		# Update over the network

		if sels:
			utime = self._get_mtime('last-checked', warn_if_missing = True)
			last_solve = max(self._get_mtime('last-solve', warn_if_missing = False), utime)

			# Ideally, this would return all the files which were inputs into the solver's
			# decision. Currently, we approximate with:
			# - the previously selected feed files (local or cached)
			# - configuration files for the selected interfaces
			# - the global configuration
			# We currently ignore feeds and interfaces which were
			# considered but not selected.
			# Can yield None (ignored), paths or (path, mtime) tuples.
			# If this throws an exception, we will log it and resolve anyway.
			def get_inputs():
				for sel in sels.selections.values():
					logger.info("Checking %s", sel.feed)

					if sel.feed.startswith('distribution:'):
						# If the package has changed version, we'll detect that below
						# with get_unavailable_selections.
						pass
					elif os.path.isabs(sel.feed):
						# Local feed
						yield sel.feed
					else:
						# Cached feed
						cached = basedir.load_first_cache(namespaces.config_site, 'interfaces', model.escape(sel.feed))
						if cached:
							yield cached
						else:
							raise IOError("Input %s missing; update" % sel.feed)

					# Per-feed configuration
					yield basedir.load_first_config(namespaces.config_site, namespaces.config_prog,
									   'interfaces', model._pretty_escape(sel.interface))

				# Global configuration
				yield basedir.load_first_config(namespaces.config_site, namespaces.config_prog, 'global')

			# If any of the feeds we used have been updated since the last check, do a quick re-solve
			try:
				for item in get_inputs():
					if not item: continue
					if isinstance(item, tuple):
						path, mtime = item
					else:
						path = item
						try:
							mtime = os.stat(path).st_mtime
						except OSError as ex:
							logger.info("Triggering update to {app} due to error: {ex}".format(
								app = self, path = path, ex = ex))
							need_solve = True
							break

					if mtime and mtime > last_solve:
						logger.info("Triggering update to %s because %s has changed", self, path)
						need_solve = True
						break
			except Exception as ex:
				logger.info("Error checking modification times: %s", ex)
				need_solve = True
				need_update = True

			# Is it time for a background update anyway?
			if not need_update:
				staleness = time.time() - utime
				logger.info("Staleness of app %s is %d hours", self, staleness / (60 * 60))
				freshness_threshold = self.config.freshness
				if freshness_threshold > 0 and staleness >= freshness_threshold:
					need_update = True

			# If any of the saved selections aren't available then we need
			# to download right now, not later in the background.
			unavailable_selections = sels.get_unavailable_selections(config = self.config, include_packages = True)
			if unavailable_selections:
				logger.info("Saved selections are unusable (missing %s)",
					    ', '.join(str(s) for s in unavailable_selections))
				need_solve = True
		else:
			# No current selections
			need_solve = True
			unavailable_selections = True

		if need_solve:
			from zeroinstall.injector.driver import Driver
			driver = Driver(config = self.config, requirements = self.get_requirements())
			if driver.need_download():
				if unavailable_selections:
					return self._foreground_update(driver, use_gui)
				else:
					# Continue with the current (cached) selections while we download
					need_update = True
			else:
				old_sels = sels
				sels = driver.solver.selections
				from zeroinstall.support import xmltools
				if old_sels is None or not xmltools.nodes_equal(sels.toDOM(), old_sels.toDOM()):
					self.set_selections(sels, set_last_checked = False)
			try:
				self._touch('last-solve')
			except OSError as ex:
				logger.warning("Error checking for updates: %s", ex)

		# If we tried to check within the last hour, don't try again.
		if need_update:
			last_check_attempt = self._get_mtime('last-check-attempt', warn_if_missing = False)
			if last_check_attempt and last_check_attempt + 60 * 60 > time.time():
				logger.info("Tried to check within last hour; not trying again now")
				need_update = False

		if need_update:
			try:
				self.set_last_check_attempt()
			except OSError as ex:
				logger.warning("Error checking for updates: %s", ex)
			else:
				from zeroinstall.injector import background
				r = self.get_requirements()
				background.spawn_background_update2(r, False, self)

		return sels
Esempio n. 9
0
def handle(config, options, args):
	if len(args) != 1:
		raise UsageError()

	assert not options.offline

	old_gui = options.gui

	app = config.app_mgr.lookup_app(args[0], missing_ok = True)
	if app is not None:
		old_sels = app.get_selections()
		old_selections = old_sels.selections
		iface_uri = old_sels.interface
		r = app.get_requirements()
		r.parse_update_options(options)
	else:
		iface_uri = model.canonical_iface_uri(args[0])

		r = requirements.Requirements(iface_uri)
		r.parse_options(options)

		# Select once in offline console mode to get the old values
		options.offline = True
		options.gui = False
		options.refresh = False

		try:
			old_sels = select.get_selections_for(r, config, options,
						select_only = True, download_only = False, test_callback = None)
		except SafeException:
			old_selections = {}
		else:
			if old_sels is None:
				old_selections = {}
			else:
				old_selections = old_sels.selections

	# Download in online mode to get the new values
	config.network_use = model.network_full
	options.offline = False
	options.gui = old_gui
	options.refresh = True

	sels = select.get_selections_for(r, config, options,
				select_only = False, download_only = True, test_callback = None)
	if not sels:
		sys.exit(1)	# Aborted by user

	root_feed = config.iface_cache.get_feed(iface_uri)
	if root_feed:
		target = root_feed.get_replaced_by()
		if target is not None:
			print(_("Warning: interface {old} has been replaced by {new}".format(old = iface_uri, new = target)))

	from zeroinstall.cmd import whatchanged
	changes = whatchanged.show_changes(old_selections, sels.selections)
	root_sel = sels[iface_uri]

	if not changes:
		from zeroinstall.support import xmltools
		# No obvious changes, but check for more subtle updates.
		if not xmltools.nodes_equal(sels.toDOM(), old_sels.toDOM()):
			changes = True
			print(_("Updates to metadata found, but no change to version ({version}).").format(version = root_sel.version))

	root_iface = config.iface_cache.get_interface(iface_uri)
	# Force a reload, since we may have used the GUI to update it
	for feed in config.iface_cache.get_feeds(root_iface):
		config.iface_cache.get_feed(feed, force = True)
	
	root_impls = config.iface_cache.get_implementations(root_iface)

	latest = max((impl.version, impl) for impl in root_impls)[1]
	if latest.version > model.parse_version(sels[iface_uri].version):
		print(_("A later version ({name} {latest}) exists but was not selected. Using {version} instead.").format(
				latest = latest.get_version(),
				name = root_iface.get_name(),
				version = root_sel.version))
		if not config.help_with_testing and latest.get_stability() < model.stable:
			print(_('To select "testing" versions, use:\n0install config help_with_testing True'))
	elif not changes:
		print(_("No updates found. Continuing with version {version}.").format(version = root_sel.version))

	if app is not None:
		if changes:
			app.set_selections(sels)
		app.set_requirements(r)
Esempio n. 10
0
def _check_for_updates(requirements, verbose, app):
    """@type requirements: L{zeroinstall.injector.requirements.Requirements}
	@type verbose: bool
	@type app: L{zeroinstall.apps.App}"""
    if app is not None:
        old_sels = app.get_selections()

    from zeroinstall.injector.driver import Driver
    from zeroinstall.injector.config import load_config

    background_handler = BackgroundHandler(requirements.interface_uri,
                                           requirements.interface_uri)
    background_config = load_config(background_handler)
    root_iface = background_config.iface_cache.get_interface(
        requirements.interface_uri).get_name()
    background_handler.title = root_iface

    driver = Driver(config=background_config, requirements=requirements)

    logger.info(_("Checking for updates to '%s' in a background process"),
                root_iface)
    if verbose:
        background_handler.notify("Zero Install",
                                  _("Checking for updates to '%s'...") %
                                  root_iface,
                                  timeout=1)

    network_state = background_handler.get_network_state()
    if network_state not in (_NetworkState.NM_STATE_CONNECTED_SITE,
                             _NetworkState.NM_STATE_CONNECTED_GLOBAL):
        logger.info(
            _("Not yet connected to network (status = %d). Sleeping for a bit..."
              ), network_state)
        import time
        time.sleep(120)
        if network_state in (_NetworkState.NM_STATE_DISCONNECTED,
                             _NetworkState.NM_STATE_ASLEEP):
            logger.info(_("Still not connected to network. Giving up."))
            sys.exit(1)
    else:
        logger.info(_("NetworkManager says we're on-line. Good!"))

    background_config.freshness = 0  # Don't bother trying to refresh when getting the interface
    refresh = driver.solve_with_downloads(
        force=True)  # (causes confusing log messages)
    tasks.wait_for_blocker(refresh)

    if background_handler.need_gui or not driver.solver.ready or driver.get_uncached_implementations(
    ):
        if verbose:
            background_handler.notify(
                "Zero Install",
                _("Updates ready to download for '%s'.") % root_iface,
                timeout=1)

        # Run the GUI if possible...
        from zeroinstall import helpers
        gui_args = ['--refresh', '--systray', '--download'
                    ] + requirements.get_as_options()
        new_sels = helpers.get_selections_gui(requirements.interface_uri,
                                              gui_args,
                                              use_gui=None)
        if new_sels is None:
            sys.exit(0)  # Cancelled by user
        elif new_sels is helpers.DontUseGUI:
            if not driver.solver.ready:
                background_handler.notify("Zero Install",
                                          _("Can't update '%s'") % root_iface)
                sys.exit(1)

            tasks.wait_for_blocker(driver.download_uncached_implementations())
            new_sels = driver.solver.selections

        if app is None:
            background_handler.notify(
                "Zero Install",
                _("{name} updated.").format(name=root_iface),
                timeout=1)
    else:
        if verbose:
            background_handler.notify("Zero Install",
                                      _("No updates to download."),
                                      timeout=1)
        new_sels = driver.solver.selections

    if app is not None:
        assert driver.solver.ready
        from zeroinstall.support import xmltools
        if not xmltools.nodes_equal(new_sels.toDOM(), old_sels.toDOM()):
            app.set_selections(new_sels)
            background_handler.notify(
                "Zero Install",
                _("{app} updated.").format(app=app.get_name()),
                timeout=1)
        app.set_last_checked()
    sys.exit(0)
Esempio n. 11
0
	def _check_for_updates(self, sels):
		"""Check whether the selections need to be updated.
		If any input feeds have changed, we re-run the solver. If the
		new selections require a download, we schedule one in the
		background and return the old selections. Otherwise, we return the
		new selections. If we can select better versions without downloading,
		we update the app's selections and return the new selections.
		We also schedule a background update from time-to-time anyway.
		@return: the selections to use
		@rtype: L{selections.Selections}"""
		need_solve = False		# Rerun solver (cached feeds have changed)
		need_update = False		# Update over the network

		utime = self._get_mtime('last-checked', warn_if_missing = True)
		last_solve = max(self._get_mtime('last-solve', warn_if_missing = False), utime)

		# Ideally, this would return all the files which were inputs into the solver's
		# decision. Currently, we approximate with:
		# - the previously selected feed files (local or cached)
		# - configuration files for the selected interfaces
		# - the global configuration
		# We currently ignore feeds and interfaces which were
		# considered but not selected.
		# Can yield None (ignored), paths or (path, mtime) tuples.
		# If this throws an exception, we will log it and resolve anyway.
		def get_inputs():
			for sel in sels.selections.values():
				logger.info("Checking %s", sel.feed)
				feed = iface_cache.get_feed(sel.feed)
				if not feed:
					raise IOError("Input %s missing; update" % sel.feed)
				else:
					if feed.local_path:
						yield feed.local_path
					else:
						yield (feed.url, feed.last_modified)

				# Per-feed configuration
				yield basedir.load_first_config(namespaces.config_site, namespaces.config_prog,
								   'interfaces', model._pretty_escape(sel.interface))

			# Global configuration
			yield basedir.load_first_config(namespaces.config_site, namespaces.config_prog, 'global')

		# If any of the feeds we used have been updated since the last check, do a quick re-solve
		iface_cache = self.config.iface_cache
		try:
			for item in get_inputs():
				if not item: continue
				if isinstance(item, tuple):
					path, mtime = item
				else:
					path = item
					mtime = os.stat(path).st_mtime

				if mtime and mtime > last_solve:
					logger.info("Triggering update to %s because %s has changed", self, path)
					need_solve = True
					break
		except Exception as ex:
			logger.info("Error checking modification times: %s", ex)
			need_solve = True
			need_update = True

		# Is it time for a background update anyway?
		if not need_update:
			staleness = time.time() - utime
			logger.info("Staleness of app %s is %d hours", self, staleness / (60 * 60))
			freshness_threshold = self.config.freshness
			if freshness_threshold > 0 and staleness >= freshness_threshold:
				need_update = True

		if need_solve:
			from zeroinstall.injector.driver import Driver
			driver = Driver(config = self.config, requirements = self.get_requirements())
			if driver.need_download():
				# Continue with the current (hopefully cached) selections while we download
				need_update = True
			else:
				old_sels = sels
				sels = driver.solver.selections
				from zeroinstall.support import xmltools
				if not xmltools.nodes_equal(sels.toDOM(), old_sels.toDOM()):
					self.set_selections(sels, set_last_checked = False)
			self._touch('last-solve')

		# If we tried to check within the last hour, don't try again.
		if need_update:
			last_check_attempt = self._get_mtime('last-check-attempt', warn_if_missing = False)
			if last_check_attempt and last_check_attempt + 60 * 60 > time.time():
				logger.info("Tried to check within last hour; not trying again now")
				need_update = False

		if need_update:
			self.set_last_check_attempt()
			from zeroinstall.injector import background
			r = self.get_requirements()
			background.spawn_background_update2(r, False, self)

		return sels
Esempio n. 12
0
    def _check_for_updates(self, sels):
        """Check whether the selections need to be updated.
		If any input feeds have changed, we re-run the solver. If the
		new selections require a download, we schedule one in the
		background and return the old selections. Otherwise, we return the
		new selections. If we can select better versions without downloading,
		we update the app's selections and return the new selections.
		We also schedule a background update from time-to-time anyway.
		@return: the selections to use
		@rtype: L{selections.Selections}"""
        need_solve = False  # Rerun solver (cached feeds have changed)
        need_update = False  # Update over the network

        utime = self._get_mtime('last-checked', warn_if_missing=True)
        last_solve = max(self._get_mtime('last-solve', warn_if_missing=False),
                         utime)

        # Ideally, this would return all the files which were inputs into the solver's
        # decision. Currently, we approximate with:
        # - the previously selected feed files (local or cached)
        # - configuration files for the selected interfaces
        # - the global configuration
        # We currently ignore feeds and interfaces which were
        # considered but not selected.
        # Can yield None (ignored), paths or (path, mtime) tuples.
        # If this throws an exception, we will log it and resolve anyway.
        def get_inputs():
            for sel in sels.selections.values():
                logger.info("Checking %s", sel.feed)
                feed = iface_cache.get_feed(sel.feed)
                if not feed:
                    raise IOError("Input %s missing; update" % sel.feed)
                else:
                    if feed.local_path:
                        yield feed.local_path
                    else:
                        yield (feed.url, feed.last_modified)

                # Per-feed configuration
                yield basedir.load_first_config(
                    namespaces.config_site, namespaces.config_prog,
                    'interfaces', model._pretty_escape(sel.interface))

            # Global configuration
            yield basedir.load_first_config(namespaces.config_site,
                                            namespaces.config_prog, 'global')

        # If any of the feeds we used have been updated since the last check, do a quick re-solve
        iface_cache = self.config.iface_cache
        try:
            for item in get_inputs():
                if not item: continue
                if isinstance(item, tuple):
                    path, mtime = item
                else:
                    path = item
                    mtime = os.stat(path).st_mtime

                if mtime and mtime > last_solve:
                    logger.info(
                        "Triggering update to %s because %s has changed", self,
                        path)
                    need_solve = True
                    break
        except Exception as ex:
            logger.info("Error checking modification times: %s", ex)
            need_solve = True
            need_update = True

        # Is it time for a background update anyway?
        if not need_update:
            staleness = time.time() - utime
            logger.info("Staleness of app %s is %d hours", self,
                        staleness / (60 * 60))
            freshness_threshold = self.config.freshness
            if freshness_threshold > 0 and staleness >= freshness_threshold:
                need_update = True

        if need_solve:
            from zeroinstall.injector.driver import Driver
            driver = Driver(config=self.config,
                            requirements=self.get_requirements())
            if driver.need_download():
                # Continue with the current (hopefully cached) selections while we download
                need_update = True
            else:
                old_sels = sels
                sels = driver.solver.selections
                from zeroinstall.support import xmltools
                if not xmltools.nodes_equal(sels.toDOM(), old_sels.toDOM()):
                    self.set_selections(sels, set_last_checked=False)
            self._touch('last-solve')

        # If we tried to check within the last hour, don't try again.
        if need_update:
            last_check_attempt = self._get_mtime('last-check-attempt',
                                                 warn_if_missing=False)
            if last_check_attempt and last_check_attempt + 60 * 60 > time.time(
            ):
                logger.info(
                    "Tried to check within last hour; not trying again now")
                need_update = False

        if need_update:
            self.set_last_check_attempt()
            from zeroinstall.injector import background
            r = self.get_requirements()
            background.spawn_background_update2(r, False, self)

        return sels
Esempio n. 13
0
    def _check_for_updates(self, sels, use_gui):
        """Check whether the selections need to be updated.
		If any input feeds have changed, we re-run the solver. If the
		new selections require a download, we schedule one in the
		background and return the old selections. Otherwise, we return the
		new selections. If we can select better versions without downloading,
		we update the app's selections and return the new selections.
		If we can't use the current selections, we update in the foreground.
		We also schedule a background update from time-to-time anyway.
		@type sels: L{zeroinstall.injector.selections.Selections}
		@type use_gui: bool
		@return: the selections to use
		@rtype: L{selections.Selections}"""
        need_solve = False  # Rerun solver (cached feeds have changed)
        need_update = False  # Update over the network

        if sels:
            utime = self._get_mtime('last-checked', warn_if_missing=True)
            last_solve = max(
                self._get_mtime('last-solve', warn_if_missing=False), utime)

            # Ideally, this would return all the files which were inputs into the solver's
            # decision. Currently, we approximate with:
            # - the previously selected feed files (local or cached)
            # - configuration files for the selected interfaces
            # - the global configuration
            # We currently ignore feeds and interfaces which were
            # considered but not selected.
            # Can yield None (ignored), paths or (path, mtime) tuples.
            # If this throws an exception, we will log it and resolve anyway.
            def get_inputs():
                for sel in sels.selections.values():
                    logger.info("Checking %s", sel.feed)

                    if sel.feed.startswith('distribution:'):
                        # If the package has changed version, we'll detect that below
                        # with get_unavailable_selections.
                        pass
                    elif os.path.isabs(sel.feed):
                        # Local feed
                        yield sel.feed
                    else:
                        # Cached feed
                        cached = basedir.load_first_cache(
                            namespaces.config_site, 'interfaces',
                            model.escape(sel.feed))
                        if cached:
                            yield cached
                        else:
                            raise IOError("Input %s missing; update" %
                                          sel.feed)

                    # Per-feed configuration
                    yield basedir.load_first_config(
                        namespaces.config_site, namespaces.config_prog,
                        'interfaces', model._pretty_escape(sel.interface))

                # Global configuration
                yield basedir.load_first_config(namespaces.config_site,
                                                namespaces.config_prog,
                                                'global')

            # If any of the feeds we used have been updated since the last check, do a quick re-solve
            try:
                for item in get_inputs():
                    if not item: continue
                    if isinstance(item, tuple):
                        path, mtime = item
                    else:
                        path = item
                        try:
                            mtime = os.stat(path).st_mtime
                        except OSError as ex:
                            logger.info(
                                "Triggering update to {app} due to error: {ex}"
                                .format(app=self, path=path, ex=ex))
                            need_solve = True
                            break

                    if mtime and mtime > last_solve:
                        logger.info(
                            "Triggering update to %s because %s has changed",
                            self, path)
                        need_solve = True
                        break
            except Exception as ex:
                logger.info("Error checking modification times: %s", ex)
                need_solve = True
                need_update = True

            # Is it time for a background update anyway?
            if not need_update:
                staleness = time.time() - utime
                logger.info("Staleness of app %s is %d hours", self,
                            staleness / (60 * 60))
                freshness_threshold = self.config.freshness
                if freshness_threshold > 0 and staleness >= freshness_threshold:
                    need_update = True

            # If any of the saved selections aren't available then we need
            # to download right now, not later in the background.
            unavailable_selections = sels.get_unavailable_selections(
                config=self.config, include_packages=True)
            if unavailable_selections:
                logger.info("Saved selections are unusable (missing %s)",
                            ', '.join(str(s) for s in unavailable_selections))
                need_solve = True
        else:
            # No current selections
            need_solve = True
            unavailable_selections = True

        if need_solve:
            from zeroinstall.injector.driver import Driver
            driver = Driver(config=self.config,
                            requirements=self.get_requirements())
            if driver.need_download():
                if unavailable_selections:
                    return self._foreground_update(driver, use_gui)
                else:
                    # Continue with the current (cached) selections while we download
                    need_update = True
            else:
                old_sels = sels
                sels = driver.solver.selections
                from zeroinstall.support import xmltools
                if old_sels is None or not xmltools.nodes_equal(
                        sels.toDOM(), old_sels.toDOM()):
                    self.set_selections(sels, set_last_checked=False)
            try:
                self._touch('last-solve')
            except OSError as ex:
                logger.warning("Error checking for updates: %s", ex)

        # If we tried to check within the last hour, don't try again.
        if need_update:
            last_check_attempt = self._get_mtime('last-check-attempt',
                                                 warn_if_missing=False)
            if last_check_attempt and last_check_attempt + 60 * 60 > time.time(
            ):
                logger.info(
                    "Tried to check within last hour; not trying again now")
                need_update = False

        if need_update:
            try:
                self.set_last_check_attempt()
            except OSError as ex:
                logger.warning("Error checking for updates: %s", ex)
            else:
                from zeroinstall.injector import background
                r = self.get_requirements()
                background.spawn_background_update2(r, False, self)

        return sels
Esempio n. 14
0
def _check_for_updates(requirements, verbose, app):
	if app is not None:
		old_sels = app.get_selections()

	from zeroinstall.injector.driver import Driver
	from zeroinstall.injector.config import load_config

	background_handler = BackgroundHandler(requirements.interface_uri, requirements.interface_uri)
	background_config = load_config(background_handler)
	root_iface = background_config.iface_cache.get_interface(requirements.interface_uri).get_name()
	background_handler.title = root_iface

	driver = Driver(config = background_config, requirements = requirements)

	logger.info(_("Checking for updates to '%s' in a background process"), root_iface)
	if verbose:
		background_handler.notify("Zero Install", _("Checking for updates to '%s'...") % root_iface, timeout = 1)

	network_state = background_handler.get_network_state()
	if network_state not in (_NetworkState.NM_STATE_CONNECTED_SITE, _NetworkState.NM_STATE_CONNECTED_GLOBAL):
		logger.info(_("Not yet connected to network (status = %d). Sleeping for a bit..."), network_state)
		import time
		time.sleep(120)
		if network_state in (_NetworkState.NM_STATE_DISCONNECTED, _NetworkState.NM_STATE_ASLEEP):
			logger.info(_("Still not connected to network. Giving up."))
			sys.exit(1)
	else:
		logger.info(_("NetworkManager says we're on-line. Good!"))

	background_config.freshness = 0			# Don't bother trying to refresh when getting the interface
	refresh = driver.solve_with_downloads(force = True)	# (causes confusing log messages)
	tasks.wait_for_blocker(refresh)

	if background_handler.need_gui or driver.get_uncached_implementations():
		if verbose:
			background_handler.notify("Zero Install",
					      _("Updates ready to download for '%s'.") % root_iface,
					      timeout = 1)

		# Run the GUI if possible...
		from zeroinstall import helpers
		gui_args = ['--refresh', '--systray', '--download'] + requirements.get_as_options()
		new_sels = helpers.get_selections_gui(requirements.interface_uri, gui_args, use_gui = None)
		if new_sels is None:
			sys.exit(0)	# Cancelled by user
		elif new_sels is helpers.DontUseGUI:
			tasks.wait_for_blocker(driver.download_uncached_implementations())
			new_sels = driver.solver.selections

		if app is None:
			background_handler.notify("Zero Install",
					      _("{name} updated.").format(name = root_iface),
					      timeout = 1)
	else:
		if verbose:
			background_handler.notify("Zero Install", _("No updates to download."), timeout = 1)
		new_sels = driver.solver.selections

	if app is not None:
		assert driver.solver.ready
		from zeroinstall.support import xmltools
		if not xmltools.nodes_equal(new_sels.toDOM(), old_sels.toDOM()):
			app.set_selections(new_sels)
			background_handler.notify("Zero Install",
					      _("{app} updated.").format(app = app.get_name()),
					      timeout = 1)
		app.set_last_checked()
	sys.exit(0)