def validate_info_config(cls, info_config): """Validate contents in suite info file.""" reports = DefaultValidators().validate( info_config, load_meta_config(info_config, config_type=rose.INFO_CONFIG_NAME)) if reports: raise SuiteInfoError(reports)
def check_fields(self, info_config, interactive_mode, from_id=None, optional_file=None, prefix=None, error_reported=False): """Check the fields in the info config""" for key in ["owner", "project", "title"]: if not info_config.get([key], no_ignore=True): if optional_file is not None: raise SuiteInfoFieldError(key) info_config_new = info_config error_reported = True if interactive_mode: question = ("rose-suite.info: \n compulsory field \"%s\"" + " not defined,\n try again?") % key try: response = raw_input(question + " y/n (default n) ") except EOFError: sys.exit(1) if response != 'y': sys.exit(1) return info_config_new, error_reported else: raise SuiteInfoFieldError(key) meta_config = load_meta_config(info_config, directory=None, config_type=rose.INFO_CONFIG_NAME, error_handler=None, ignore_meta_error=False) reports = DefaultValidators().validate(info_config, meta_config) if reports != []: reports_map = {None: reports} text = (rose.macro.get_reports_as_text (reports_map, "rose.macro.DefaultValidators")) if interactive_mode: reporter = rose.reporter.Reporter() reporter(text, kind=reporter.KIND_ERR, level=reporter.FAIL, prefix="") info_config_new = info_config error_reported = True question = "Metadata issue, do you want to try again?" try: response = raw_input(question + " y/n (default n) ") except EOFError: sys.exit(1) if response != 'y': sys.exit(1) return info_config_new, error_reported else: raise SuiteInfoError(text) elif error_reported: return None, error_reported elif error_reported is False: return info_config, error_reported if from_id is not None: return self._copy(info_config, from_id) return info_config, error_reported
def create(argv): """CLI function: create and copy.""" opt_parser = RoseOptionParser() opt_parser.add_my_options( "checkout_mode", "info_file", "meta_suite_mode", "non_interactive", "prefix", "project") opts, args = opt_parser.parse_args(argv) verbosity = opts.verbosity - opts.quietness client = RosieVCClient(event_handler=Reporter(verbosity)) SuiteId.svn.event_handler = client.event_handler from_id = None if args: from_id = SuiteId(id_text=args[0]) if from_id.branch is None: from_id.branch = from_id.BRANCH_TRUNK if from_id.revision is None: from_id.revision = from_id.REV_HEAD from_id = SuiteId(id_text=from_id.to_string_with_version()) interactive_mode = not opts.non_interactive if opts.info_file is None: info_config = client.generate_info_config( from_id, opts.prefix, opts.project) if from_id is not None: meta_config = load_meta_config( info_config, directory=None, config_type=rose.INFO_CONFIG_NAME, error_handler=None, ignore_meta_error=False) for node_keys, node in meta_config.walk(no_ignore=True): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value sect = sect.replace("=", "") if key == "copy-mode" and value == "clear": info_config.set([sect], "") if key == "copy-mode" and value == "never": info_config.unset([sect]) info_config = _edit_info_config(opts, client, info_config) else: file_ = opts.info_file if opts.info_file == "-": file_ = sys.stdin info_config = rose.config.load(file_) info_config = _validate_info_config(opts, client, info_config) if interactive_mode: prefix = opts.prefix if from_id: if not prefix: prefix = from_id.prefix question = PROMPT_COPY % (from_id.to_string_with_version(), prefix) else: if not prefix: prefix = SuiteId.get_prefix_default() question = PROMPT_CREATE % prefix try: response = input(question) except EOFError: sys.exit(1) if response != YES: sys.exit(1) try: id_ = client.create( info_config, from_id, opts.prefix, opts.meta_suite_mode) except (RosePopenError, SuiteIdOverflowError) as exc: client.event_handler(exc) sys.exit(1) if opts.checkout_mode: try: client.checkout(id_) except (FileExistError, RosePopenError) as exc: client.event_handler(exc) sys.exit(1)
def generate_info_config(self, from_id=None, prefix=None, project=None): """Generate a rose.config.ConfigNode for a rose-suite.info. This is suitable for passing into the create method of this class. If from_id is defined, copy items from it. Return the rose.config.ConfigNode instance. """ from_project = None from_title = None if from_id is not None: from_info_url = "%s/%s/rose-suite.info@%s" % (from_id.to_origin(), from_id.branch, from_id.revision) out_data = self.popen("svn", "cat", from_info_url)[0] from_config = rose.config.load(StringIO(out_data.decode())) res_loc = ResourceLocator.default() older_config = None info_config = rose.config.ConfigNode() # Determine project if given as a command-line option on create if from_id is None and project is not None: info_config.set(["project"], project) # Set the compulsory fields and use the project and metadata if # available. meta_config = load_meta_config( info_config, config_type=rose.INFO_CONFIG_NAME) if from_id is None and project is not None: for node_keys, node in meta_config.walk(no_ignore=True): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value sect = sect.translate(None, "=") if key == "compulsory" and value == "true": info_config.set([sect], "") info_config.set(["project"], project) else: if from_project is None: info_config.set(["project"], "") if from_title is None: info_config.set(["title"], "") # Determine prefix if prefix is None: if from_id is None: prefix = SuiteId.get_prefix_default() else: prefix = from_id.prefix # Determine owner: # 1. From user configuration [rosie-id]prefix-username # 2. From username of a matching group in [groups] in # ~/.subversion/servers # 3. Current user ID owner = res_loc.get_conf().get_value( ["rosie-id", "prefix-username." + prefix]) if not owner and self.subversion_servers_conf: servers_conf = rose.config.load(self.subversion_servers_conf) groups_node = servers_conf.get(["groups"]) if groups_node is not None: prefix_loc = SuiteId.get_prefix_location(prefix) prefix_host = urlparse(prefix_loc).hostname for key, node in groups_node.value.items(): if fnmatch(prefix_host, node.value): owner = servers_conf.get_value([key, "username"]) break if not owner: owner = pwd.getpwuid(os.getuid())[0] info_config.set(["owner"], owner) # Copy description try: from_id.to_string_with_version() info_config.set( ["description"], "Copy of %s" % (from_id.to_string_with_version())) except AttributeError: pass # Copy fields provided by the user try: from_config.walk(no_ignore=False) for node_keys, node in from_config.walk(no_ignore=False): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value if (key in ["description", "owner", "access-list"] or (key == "project" and from_project is not None)): pass else: info_config.set([key], value) except UnboundLocalError: pass # Determine access list access_list_str = res_loc.get_conf().get_value( ["rosie-vc", "access-list-default"]) if access_list_str: info_config.set(["access-list"], access_list_str) if from_id is None and project is not None: for node_keys, node in meta_config.walk(no_ignore=True): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value sect = sect.translate(None, "=") if key == "value-hints" or key == "values": reminder = ("please remove all commented hints/lines " + "in the main/top section before saving.") info_config.set([sect], rose.variable.array_split(value)[0], comments=[value, reminder]) if older_config is not None: for node_keys, node in older_config.walk(no_ignore=True): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value info_config.set([key], value) return info_config
def run(self, repos, txn): """Apply the rule engine on transaction "txn" to repository "repos".""" changes = set() # set([(status, path), ...]) for line in self._svnlook("changed", "-t", txn, repos).splitlines(): status, path = line.split(None, 1) changes.add((status, path)) bad_changes = [] author = None super_users = None rev_info_map = {} # {path-id: (owner, access-list), ...} txn_info_map = {} conf = ResourceLocator.default().get_conf() ignores_str = conf.get_value(["rosa-svn", "ignores"], self.IGNORES) ignores = shlex.split(ignores_str) for status, path in sorted(changes): if any([fnmatch(path, ignore) for ignore in ignores]): continue names = path.split("/", self.LEN_ID + 1) if not names[-1]: names.pop() # Directories above the suites must match the ID patterns is_bad = False for name, pattern in zip(names, self.RE_ID_NAMES): if not re.compile(r"\A" + pattern + r"\Z").match(name): is_bad = True break if is_bad: bad_changes.append(BadChange(status, path)) continue # Can only add directories at levels above the suites if len(names) < self.LEN_ID: if status[0] != self.ST_ADD: bad_changes.append(BadChange(status, path)) continue else: is_meta_suite = "".join(names[0:self.LEN_ID]) == "ROSIE" # No need to check non-trunk changes if len(names) > self.LEN_ID and names[self.LEN_ID] != "trunk": continue # New suite should have an info file if status[0] == self.ST_ADD and len(names) == self.LEN_ID: if (self.ST_ADD, path + "trunk/") not in changes: bad_changes.append( BadChange(status, path, BadChange.NO_TRUNK)) continue path_trunk_info_file = path + self.TRUNK_INFO_FILE if ((self.ST_ADD, path_trunk_info_file) not in changes and (self.ST_UPDATE, path_trunk_info_file) not in changes): bad_changes.append( BadChange(status, path, BadChange.NO_INFO)) continue # The rest are trunk changes in a suite path_head = "/".join(names[0:self.LEN_ID]) + "/" path_tail = path[len(path_head):] # For meta suite, make sure keys in keys file can be parsed if is_meta_suite and path_tail == self.TRUNK_KNOWN_KEYS_FILE: out = self._svnlook("cat", "-t", txn, repos, path) try: shlex.split(out) except ValueError: bad_changes.append( BadChange(status, path, BadChange.VALUE)) continue # Suite trunk information file must have an owner # User IDs of owner and access list must be real if (status not in self.ST_DELETE and path_tail == self.TRUNK_INFO_FILE): if path_head not in txn_info_map: txn_info_map[path_head] = self._get_info( repos, path_head, txn) txn_owner, txn_access_list = self._get_access_info( txn_info_map[path_head]) if not txn_owner: bad_changes.append( BadChange(status, path, BadChange.NO_OWNER)) continue if self._verify_users( status, path, txn_owner, txn_access_list, bad_changes): continue reports = DefaultValidators().validate( txn_info_map[path_head], load_meta_config( txn_info_map[path_head], config_type=rose.INFO_CONFIG_NAME)) if reports: reports_str = get_reports_as_text({None: reports}, path) bad_changes.append( BadChange(status, path, BadChange.VALUE, reports_str)) continue # Can only remove trunk information file with suite if status == self.ST_DELETE and path_tail == self.TRUNK_INFO_FILE: if (self.ST_DELETE, path_head) not in changes: bad_changes.append( BadChange(status, path, BadChange.NO_INFO)) continue # Can only remove trunk with suite if status == self.ST_DELETE and path_tail == "trunk/": if (self.ST_DELETE, path_head) not in changes: bad_changes.append( BadChange(status, path, BadChange.NO_TRUNK)) continue # New suite trunk: ignore the rest if (self.ST_ADD, path_head + "trunk/") in changes: continue # See whether author has permission to make changes if author is None: author = self._svnlook("author", "-t", txn, repos).strip() if super_users is None: super_users = [] for s_key in ["rosa-svn", "rosa-svn-pre-commit"]: value = conf.get_value([s_key, "super-users"]) if value is not None: super_users = shlex.split(value) break if path_head not in rev_info_map: rev_info_map[path_head] = self._get_info( repos, path_head) owner, access_list = self._get_access_info(rev_info_map[path_head]) admin_users = super_users + [owner] # Only admin users can remove the suite if author not in admin_users and not path_tail: bad_changes.append(BadChange(status, path)) continue # Admin users and those in access list can modify everything in # trunk apart from specific entries in the trunk info file if "*" in access_list or author in admin_users + access_list: if path_tail != self.TRUNK_INFO_FILE: continue else: bad_changes.append(BadChange(status, path)) continue # The owner must not be deleted if path_head not in txn_info_map: txn_info_map[path_head] = self._get_info( repos, path_head, txn) txn_owner, txn_access_list = self._get_access_info( txn_info_map[path_head]) if not txn_owner: bad_changes.append(BadChange(status, path, BadChange.NO_OWNER)) continue # Only the admin users can change owner and access list if owner == txn_owner and access_list == txn_access_list: continue if author not in admin_users: if owner != txn_owner: bad_changes.append(BadChange(status, path, BadChange.PERM, "owner=" + txn_owner)) else: # access list bad_change = BadChange( status, path, BadChange.PERM, "access-list=" + " ".join(txn_access_list)) bad_changes.append(bad_change) continue if bad_changes: raise BadChanges(bad_changes)
def create(argv): """CLI function: create and copy.""" opt_parser = RoseOptionParser() opt_parser.add_my_options("checkout_mode", "info_file", "meta_suite_mode", "non_interactive", "prefix", "project") opts, args = opt_parser.parse_args(argv) verbosity = opts.verbosity - opts.quietness client = RosieVCClient(event_handler=Reporter(verbosity)) SuiteId.svn.event_handler = client.event_handler from_id = None if args: from_id = SuiteId(id_text=args[0]) if from_id.branch is None: from_id.branch = from_id.BRANCH_TRUNK if from_id.revision is None: from_id.revision = from_id.REV_HEAD from_id = SuiteId(id_text=from_id.to_string_with_version()) interactive_mode = not opts.non_interactive if opts.info_file is None: info_config = client.generate_info_config(from_id, opts.prefix, opts.project) if from_id is not None: meta_config = load_meta_config(info_config, directory=None, config_type=rose.INFO_CONFIG_NAME, error_handler=None, ignore_meta_error=False) for node_keys, node in meta_config.walk(no_ignore=True): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value sect = sect.translate(None, "=") if key == "copy-mode" and value == "clear": info_config.set([sect], "") if key == "copy-mode" and value == "never": info_config.unset([sect]) info_config = _edit_info_config(opts, client, info_config) else: file_ = opts.info_file if opts.info_file == "-": file_ = sys.stdin info_config = rose.config.load(file_) info_config = _validate_info_config(opts, client, info_config) if interactive_mode: prefix = opts.prefix if from_id: if not prefix: prefix = from_id.prefix question = PROMPT_COPY % (from_id.to_string_with_version(), prefix) else: if not prefix: prefix = SuiteId.get_prefix_default() question = PROMPT_CREATE % prefix try: response = raw_input(question) except EOFError: sys.exit(1) if response != YES: sys.exit(1) try: id_ = client.create(info_config, from_id, opts.prefix, opts.meta_suite_mode) except (RosePopenError, SuiteIdOverflowError) as exc: client.event_handler(exc) sys.exit(1) if opts.checkout_mode: try: client.checkout(id_) except (FileExistError, RosePopenError) as exc: client.event_handler(exc) sys.exit(1)
def generate_info_config(self, from_id=None, prefix=None, project=None): """Generate a rose.config.ConfigNode for a rose-suite.info. This is suitable for passing into the create method of this class. If from_id is defined, copy items from it. Return the rose.config.ConfigNode instance. """ from_project = None from_title = None if from_id is not None: from_info_url = "%s/%s/rose-suite.info@%s" % ( from_id.to_origin(), from_id.branch, from_id.revision) out_data = self.popen("svn", "cat", from_info_url)[0] from_config = rose.config.load(StringIO(out_data)) res_loc = ResourceLocator.default() older_config = None info_config = rose.config.ConfigNode() # Determine project if given as a command-line option on create if from_id is None and project is not None: info_config.set(["project"], project) # Set the compulsory fields and use the project and metadata if # available. meta_config = load_meta_config(info_config, config_type=rose.INFO_CONFIG_NAME) if from_id is None and project is not None: for node_keys, node in meta_config.walk(no_ignore=True): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value sect = sect.translate(None, "=") if key == "compulsory" and value == "true": info_config.set([sect], "") info_config.set(["project"], project) else: if from_project is None: info_config.set(["project"], "") if from_title is None: info_config.set(["title"], "") # Determine prefix if prefix is None: if from_id is None: prefix = SuiteId.get_prefix_default() else: prefix = from_id.prefix # Determine owner: # 1. From user configuration [rosie-id]prefix-username # 2. From username of a matching group in [groups] in # ~/.subversion/servers # 3. Current user ID owner = res_loc.get_conf().get_value( ["rosie-id", "prefix-username." + prefix]) if not owner and self.subversion_servers_conf: servers_conf = rose.config.load(self.subversion_servers_conf) groups_node = servers_conf.get(["groups"]) if groups_node is not None: prefix_loc = SuiteId.get_prefix_location(prefix) prefix_host = urlparse(prefix_loc).hostname for key, node in groups_node.value.items(): if fnmatch(prefix_host, node.value): owner = servers_conf.get_value([key, "username"]) break if not owner: owner = pwd.getpwuid(os.getuid())[0] info_config.set(["owner"], owner) # Copy description try: from_id.to_string_with_version() info_config.set(["description"], "Copy of %s" % (from_id.to_string_with_version())) except AttributeError: pass # Copy fields provided by the user try: from_config.walk(no_ignore=False) for node_keys, node in from_config.walk(no_ignore=False): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value if (key in ["description", "owner", "access-list"] or (key == "project" and from_project is not None)): pass else: info_config.set([key], value) except UnboundLocalError: pass # Determine access list access_list_str = res_loc.get_conf().get_value( ["rosie-vc", "access-list-default"]) if access_list_str: info_config.set(["access-list"], access_list_str) if from_id is None and project is not None: for node_keys, node in meta_config.walk(no_ignore=True): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value sect = sect.translate(None, "=") if key == "value-hints" or key == "values": reminder = ("please remove all commented hints/lines " + "in the main/top section before saving.") info_config.set([sect], rose.variable.array_split(value)[0], comments=[value, reminder]) if older_config is not None: for node_keys, node in older_config.walk(no_ignore=True): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value info_config.set([key], value) return info_config
def run(self, repos, txn): """Apply the rule engine on transaction "txn" to repository "repos".""" changes = set() # set([(status, path), ...]) for line in self._svnlook("changed", "-t", txn, repos).splitlines(): status, path = line.split(None, 1) changes.add((status, path)) bad_changes = [] author = None super_users = None rev_info_map = {} # {path-id: (owner, access-list), ...} txn_info_map = {} conf = ResourceLocator.default().get_conf() ignores_str = conf.get_value(["rosa-svn", "ignores"], self.IGNORES) ignores = shlex.split(ignores_str) for status, path in sorted(changes): if any(fnmatch(path, ignore) for ignore in ignores): continue names = path.split("/", self.LEN_ID + 1) tail = None if not names[-1]: tail = names.pop() # Directories above the suites must match the ID patterns is_bad = False for name, pattern in zip(names, self.RE_ID_NAMES): if not re.compile(r"\A" + pattern + r"\Z").match(name): is_bad = True break if is_bad: bad_changes.append(BadChange(status, path)) continue # Can only add directories at levels above the suites if len(names) < self.LEN_ID: if status[0] != self.ST_ADD: bad_changes.append(BadChange(status, path)) continue else: is_meta_suite = "".join(names[0:self.LEN_ID]) == "ROSIE" # A file at the branch level if len(names) == self.LEN_ID + 1 and tail is None: bad_changes.append(BadChange(status, path)) continue # No need to check non-trunk changes if len(names) > self.LEN_ID and names[self.LEN_ID] != "trunk": continue # New suite should have an info file if status[0] == self.ST_ADD and len(names) == self.LEN_ID: if (self.ST_ADD, path + "trunk/") not in changes: bad_changes.append( BadChange(status, path, BadChange.NO_TRUNK)) continue path_trunk_info_file = path + self.TRUNK_INFO_FILE if ((self.ST_ADD, path_trunk_info_file) not in changes and (self.ST_UPDATE, path_trunk_info_file) not in changes): bad_changes.append( BadChange(status, path, BadChange.NO_INFO)) continue # The rest are trunk changes in a suite path_head = "/".join(names[0:self.LEN_ID]) + "/" path_tail = path[len(path_head):] # For meta suite, make sure keys in keys file can be parsed if is_meta_suite and path_tail == self.TRUNK_KNOWN_KEYS_FILE: out = self._svnlook("cat", "-t", txn, repos, path) try: shlex.split(out) except ValueError: bad_changes.append(BadChange(status, path, BadChange.VALUE)) continue # Suite trunk information file must have an owner # User IDs of owner and access list must be real if (status not in self.ST_DELETE and path_tail == self.TRUNK_INFO_FILE): if path_head not in txn_info_map: txn_info_map[path_head] = self._get_info( repos, path_head, txn) txn_owner, txn_access_list = self._get_access_info( txn_info_map[path_head]) if not txn_owner: bad_changes.append( BadChange(status, path, BadChange.NO_OWNER)) continue if self._verify_users(status, path, txn_owner, txn_access_list, bad_changes): continue reports = DefaultValidators().validate( txn_info_map[path_head], load_meta_config(txn_info_map[path_head], config_type=rose.INFO_CONFIG_NAME)) if reports: reports_str = get_reports_as_text({None: reports}, path) bad_changes.append( BadChange(status, path, BadChange.VALUE, reports_str)) continue # Can only remove trunk information file with suite if status == self.ST_DELETE and path_tail == self.TRUNK_INFO_FILE: if (self.ST_DELETE, path_head) not in changes: bad_changes.append( BadChange(status, path, BadChange.NO_INFO)) continue # Can only remove trunk with suite if status == self.ST_DELETE and path_tail == "trunk/": if (self.ST_DELETE, path_head) not in changes: bad_changes.append( BadChange(status, path, BadChange.NO_TRUNK)) continue # New suite trunk: ignore the rest if (self.ST_ADD, path_head + "trunk/") in changes: continue # See whether author has permission to make changes if author is None: author = self._svnlook("author", "-t", txn, repos).strip() if super_users is None: super_users = [] for s_key in ["rosa-svn", "rosa-svn-pre-commit"]: value = conf.get_value([s_key, "super-users"]) if value is not None: super_users = shlex.split(value) break if path_head not in rev_info_map: rev_info_map[path_head] = self._get_info(repos, path_head) owner, access_list = self._get_access_info(rev_info_map[path_head]) admin_users = super_users + [owner] # Only admin users can remove the suite if author not in admin_users and not path_tail: bad_changes.append(BadChange(status, path)) continue # Admin users and those in access list can modify everything in # trunk apart from specific entries in the trunk info file if "*" in access_list or author in admin_users + access_list: if path_tail != self.TRUNK_INFO_FILE: continue else: bad_changes.append(BadChange(status, path)) continue # The owner must not be deleted if path_head not in txn_info_map: txn_info_map[path_head] = self._get_info(repos, path_head, txn) txn_owner, txn_access_list = self._get_access_info( txn_info_map[path_head]) if not txn_owner: bad_changes.append(BadChange(status, path, BadChange.NO_OWNER)) continue # Only the admin users can change owner and access list if owner == txn_owner and access_list == txn_access_list: continue if author not in admin_users: if owner != txn_owner: bad_changes.append( BadChange(status, path, BadChange.PERM, "owner=" + txn_owner)) else: # access list bad_change = BadChange( status, path, BadChange.PERM, "access-list=" + " ".join(txn_access_list)) bad_changes.append(bad_change) continue if bad_changes: raise BadChanges(bad_changes)
def create(argv): """CLI function: create and copy.""" opt_parser = RoseOptionParser() opt_parser.add_my_options("checkout_mode", "info_file", "meta_suite_mode", "non_interactive", "prefix", "project") opts, args = opt_parser.parse_args(argv) verbosity = opts.verbosity - opts.quietness report = Reporter(verbosity) client = RosieVCClient(event_handler=report) SuiteId.svn.event_handler = client.event_handler # FIXME: ugly? from_id = None if args: from_id = SuiteId(id_text=args[0]) if from_id.branch is None: from_id.branch = from_id.BRANCH_TRUNK if from_id.revision is None: from_id.revision = from_id.REV_HEAD from_id = SuiteId(id_text=from_id.to_string_with_version()) info_config_new = None interactive_mode = not opts.non_interactive if opts.info_file is None: try: info_config = client.generate_info_config(from_id, opts.prefix, opts.project) except (RosePopenError) as e: report(e) sys.exit(1) info_file = tempfile.NamedTemporaryFile() if args: meta_config = load_meta_config(info_config, directory=None, config_type=rose.INFO_CONFIG_NAME, error_handler=None, ignore_meta_error=False) for node_keys, node in meta_config.walk(no_ignore=True): if isinstance(node.value, dict): continue sect, key = node_keys value = node.value sect = sect.translate(None, "=") if key == "copy-mode" and value == "clear": info_config.set([sect], "") if key == "copy-mode" and value == "never": info_config.unset([sect]) rose.config.dump(info_config, info_file) info_file.write(CREATE_INFO_CONFIG_COMMENT) info_file.seek(0) command_list = client.popen.get_cmd("editor", info_file.name) client.popen(*command_list, stdout=sys.stdout) info_config = rose.config.load(info_file) try: info_config_new, error_reported = client.check_fields(info_config, interactive_mode, from_id, opts.prefix) except (RosePopenError, SuiteInfoFieldError, SuiteIdOverflowError) as e: report(e) sys.exit(1) while error_reported is True: info_file = tempfile.NamedTemporaryFile() info_config = info_config_new if (info_config.get(["project"]).value is not None and opts.project is None): project = info_config.get(["project"]).value info_config = client.generate_info_config(from_id, opts.prefix, project, info_config) rose.config.dump(info_config, info_file) info_file.write(CREATE_INFO_CONFIG_COMMENT) info_file.seek(0) command_list = client.popen.get_cmd("editor", info_file.name) client.popen(*command_list, stdout=sys.stdout) info_config = rose.config.load(info_file) try: info_config_new, error_reported = client.check_fields( info_config, interactive_mode, from_id, opts.prefix) except (RosePopenError, SuiteInfoFieldError, SuiteIdOverflowError) as e: report(e) sys.exit(1) elif opts.info_file == "-": info_config = rose.config.load(sys.stdin) try: info_config_new, error_reported = client.check_fields(info_config, interactive_mode, from_id, opts.info_file, opts.prefix) except (RosePopenError, SuiteInfoFieldError, SuiteIdOverflowError) as e: report(e) sys.exit(1) else: info_config = rose.config.load(opts.info_file) try: info_config_new, error_reported = client.check_fields(info_config, interactive_mode, from_id, opts.info_file, opts.prefix) except (RosePopenError, SuiteInfoFieldError, SuiteIdOverflowError) as e: report(e) sys.exit(1) if interactive_mode: if from_id: question = "Copy \"%s\"?" % from_id.to_string_with_version() else: prefix = opts.prefix if not prefix: prefix = SuiteId.get_prefix_default() question = "Create suite at \"%s\"?" % prefix try: response = raw_input(question + " y/n (default n) ") except EOFError: sys.exit(1) if response != 'y': sys.exit(1) try: id = client.create(info_config, from_id, opts.prefix, opts.meta_suite_mode) except (RosePopenError, SuiteInfoFieldError, SuiteIdOverflowError) as e: report(e) sys.exit(1) if opts.checkout_mode: try: client.checkout(id) except (FileExistError, RosePopenError) as e: report(e) sys.exit(1)