def launch_convertion(self): """Convert build steps to target convertion. """ Log.debug("Converting...") Converter.check() and Converter.save(self.steps) Log.debug("Conversion done!")
def check_instruction(self, line): """Check line from build steps if it's an instruction. Args: line (str): Build step line as string. Raises: Exception: If last topic is missing and step is found. Returns: self: Self instance. """ if self.no_topic_skip and self.last_topic is None: self.last_step = None return self scan = Instruction.PATTERN.match(line) if scan is not None: step = scan.group("step") if Placeholder.scan_string(step): step = Placeholder.parse_string(step) punct = scan.group("punct") self.last_step = Instruction(self.get_line_number(), step, punct) if self.last_topic is None: Log.fatal("Unaccepted instruction: missing topic in guide") self.last_topic.add_step(self.last_step) return self
def __init__(self, args): self.args = args self.topic = None self.steps = None self.last_step = 0 self.guide_topics = None self.convert = False self.fake_run = False Log.info("Initializing...")
def unlock(): """Releases a lock file. Raises: OSError: If lock file cannot be released. """ if os.path.isfile(LOCK_FILE): os.remove(LOCK_FILE) Log.info("Removed temporary lock")
def output(cls): """Dump report of each saved property. """ ran_status = (cls.current_step, cls.total_steps) Log.info("""Topic name: "%s" """ % cls.topic) Log.info("Steps ran successful %d out of %d" % ran_status) Log.info("""Last step error: "%s" """ % cls.error) Log.info("Runtime %ss" % cls.runtime) Log.info("Build %s" % cls.status.upper())
def run(self, ignore_fails=False): """Run all steps for the current selected topic. """ if self.fake_run: Log.info("Topic '%s' faked run" % self.topic.get_title()) Report.set_status("FAKED") else: self.launch_topic(ignore_fails) if self.convert: self.launch_convertion()
def install_packages(self, cmd, packages): installed = 0 for pkg in packages: install_cmd = cmd_split(cmd.format(packages=pkg)) install_output = Popen(install_cmd) while install_output.poll() is None: sleep(0.5) output = install_output.returncode Log.debug("Running (npm) %s ... %r" % (install_cmd, output == 0)) if 0 == output: installed += 1 return installed
def lock(): """Create a lock file. Raises: SystemExit: If a lock file already exists. """ if os.path.isfile(LOCK_FILE): raise SystemExit("A build might be launched by another process") with open(LOCK_FILE, "a"): os.utime(LOCK_FILE, None) Log.info("Created temporary lock")
def install_packages(self, cmd, packages): installed = 0 for pkg in packages: install_cmd = cmd_split(cmd.format(packages=pkg)) install_output = Popen(install_cmd) while install_output.poll() is None: sleep(0.5) output = install_output.returncode log_status = (self.env.os_name, install_cmd, output == 0) Log.debug("Running (%s) %s ... %r" % log_status) if 0 == output: installed += 1 return installed
def setup(self): """Setup project path, topic and topic pattern, convertion type. """ # Look for a specific topic if self.args.topic is not None: Topic.set_project_topic(self.args.topic) Log.info("Topic changed to: %s" % self.args.topic) # Set topic pattern if self.args.topic_pattern is not None: Topic.set_project_topic_pattern(self.args.topic_pattern) Log.info("Topic pattern changed to: %s" % self.args.topic_pattern) # Change project path from current working directory to choosen path if self.args.guide is not None: Reader.set_project_path(self.args.guide) Log.info("Project guide location: %s" % self.args.guide) # Convert build steps to script before exit if self.args.convert is not None: Converter.prepare(self.args.convert) Log.info("Topic convertion set to: %s" % self.args.convert) self.convert = True # Toggle fake run self.fake_run = self.args.fake_run
def set_project_path(cls, project_path): """Change project path. Default is current working directory. Args: project_path (str): New project path. Raises: ValueError: If project path is not a string. """ if not isinstance(project_path, (str, unicode)): Log.fatal("Project path must be string") cls.PATH = project_path.rstrip("/")
def crash(message): """Crash application with message. Raises: SystemExit. """ return Log.fatal("Unexpected crash! %s" % message)
def run(self, srv=None, *args, **kwargs): cmd = StatusService.check_systemd() if cmd is None: return self.fail("Unsupported OS: %s" % self.env.os_name) try: service_cmd = cmd.format(service=srv) log_status = (self.env.os_name, service_cmd) Log.debug("Service OS (%s) status: %s ..." % log_status) ok, status = StatusService.get_service_status(service_cmd) output = u"Service '%s' => %s" % (srv, status) if ok: self.success(output) else: self.fail(output) except CalledProcessError as e: self.fail(e.output) except Exception as e: self.fail(str(e))
def run(self, srv=None, *args, **kwargs): cmd = StopService.check_systemd() if cmd is None: return self.fail("Unsupported OS: %s" % self.env.os_name) try: service_cmd = cmd.format(service=srv) log_status = (self.env.os_name, service_cmd) Log.debug("Service OS (%s) stop: %s ..." % log_status) service_output = Popen(cmd_split(service_cmd)) while service_output.poll() is None: sleep(0.5) if 0 != service_output.returncode: return self.fail(u"Service '%s' => failed to stop" % srv) self.success(u"Service '%s' => stopped" % srv) except CalledProcessError as e: self.fail(e.output) except Exception as e: self.fail(str(e))
def setup(self): """Setup if reader is properly called. Raises: ValueError: Project does not have build file. Returns: bool: Returns True if project setup is done. """ if self.READER is None or self.PATH is None: return False filepath = self.PATH if path.isdir(filepath): filepath = path.join(self.PATH, self.READER) self.filename = path.abspath(filepath) if not self.exists(): Log.fatal("Project doesn't have proper build file: %s" % filepath) return True
def parse(self, newlines=0): """Parse build steps file. Loop through all steps and check for topics and instructions. """ Log.debug("Guide has %d lines" % len(self.content)) self.last_guide = Guide() while self.has_next(): line = self.get_line(strip=True) if len(line) == 0: newlines += 1 else: newlines = 0 if newlines >= 2: self.last_topic = None self.check_topic(line) self.check_instruction(line).check_payload(self.next_content()) self.next_line()
def analyze(statement, length=80): """Analyze all statements and print visual results. Returns: bool: True if all statements are ok, otherwise False. """ lines = scan_statements(statement) results = self_analyze(lines) Log.debug("Listing all statements...") for action in statement.get_actions(): try: text = action.parse_description() except Exception: text = "no description" delimiter = "-" * (length - 4) c_txt = u"\033[96m %-{}s \033[0m".format(length - 6) r_txt = u"\033[91m %-{}s \033[0m".format(length - 6) g_txt = u"\033[92m %-{}s \033[0m".format(length - 6) print(u"\033[90m|---|%-{}s|\033[0m".format(length - 4) % delimiter) print(u"\033[90m| |\033[0m" + fmt(c_txt, text) + "\033[90m|\033[0m") print(u"\033[90m|---|%-{}s|\033[0m".format(length - 4) % delimiter) for line in action.parse_statements(): if lines[line] > 1: status = UnicodeIcon.INVALID line_text = fmt(r_txt, line.strip()) else: status = UnicodeIcon.VALID line_text = fmt(g_txt, line.strip()) print_line = u"\033[90m|\033[0m " + status + u" \033[90m|\033[0m" print_line += line_text + u"\033[90m|\033[0m" print(print_line) print(u"\033[90m|---|%-{}s|\033[0m".format(length - 4) % delimiter) if not results: Log.error("Duplicated statements found!") for line, times in lines.iteritems(): if times > 1: err = (times, line.strip()) Log.error(" - line duplicated %d times: %s" % err) Log.error("Please correct these problems before running again.") else: Log.debug("Everything looks OK!") return results
def check_payload(self, content): """Check line from build steps if it's an instruction payload. Args: content (list): Build step lines ahead of current cursor. Returns: self: Self instance. """ if self.last_step is None: return self if self.last_step.punct != Instruction.RunType.ARGS: return self newlines, borders = 0, [] for i, next_line in enumerate(content): if len(borders) == 0 and newlines > 1: raise self.NoPayloadError(self.last_step) if next_line.strip() == "": newlines += 1 continue if next_line.strip() == self.delimiter: borders.append(i) if len(borders) == 2: break if len(borders) == 1: raise self.UnclosedPayloadError(self.last_step) try: a, z = borders payload = content[1 + a:z] if Placeholder.scan_list(payload): payload = Placeholder.parse_list(payload) self.last_step.set_payload(payload) self.skip_line(lines=z + 1) self.last_step = None except Exception as e: Log.error(str(e)) raise self.BadFormatError(self.last_step) return self
def scan_lookup(statements, lookup, length=80): """Lookup a statement with example usage. Args: statements (Statement): Statement instance will all known statements. lookup (str): String to lookup after in all statements. """ Log.debug("Lookup after %s ..." % lookup) known_statements = statements.get_actions() actions = [] # Check available statements if len(known_statements) == 0: return Log.error("Statements are missing") # Search in all statemnts for stmt in known_statements: if lookup.lower() in stmt.parse_description().lower(): if stmt not in actions: actions.append(stmt) else: for exp in stmt.parse_statements(): if lookup.lower() in exp.lower(): if stmt not in actions: actions.append(stmt) # Handle missing action if len(actions) == 0: return Log.error("Nothing found for '%s'" % lookup) # Print lookup for action in actions: delimiter = "-" * (length) description = action.parse_description() desc = u"\033[96m %-{}s \033[0m".format(length - 2) % description print(u"\033[90m|%-{}s|\033[0m".format(length - 4) % delimiter) print(u"\033[90m|\033[0m" + desc + "\033[90m|\033[0m") print(u"\033[90m|%-{}s|\033[0m".format(length - 4) % delimiter) for text in action.parse_statements(): stmt = u"\033[95m %-{}s \033[0m".format(length - 6) % text.strip() print(u"\033[90m| |\033[0m" + stmt + "\033[90m|\033[0m") print(u"\033[90m|%-{}s|\033[0m".format(length - 4) % delimiter) for text in action.parse("sample"): sample = u"\033[93m %-{}s \033[0m".format(length - 6) % text.strip() print(u"\033[90m| |\033[0m" + sample + "\033[90m|\033[0m") # Done print(u"\033[90m|%-{}s|\033[0m".format(length - 4) % delimiter) Log.debug("Done...")
def prepare(cls): """Statement initialization. Loops through all supported actions, validates and maps statements with actions. Returns: bool: True if statements are mapped successful. """ Log.debug("Preparing to scan %d actions" % len(cls.__actions)) for action in cls.__actions: if not callable(action): raise SystemExit("Expected action to be callable") Log.debug("Scanning action: %s" % action.parse_description()) for line in action.parse_statements(): exp = compile(line.strip(), IGNORECASE | UNICODE) cls.statements.update({exp: action}) Log.debug("Updating known patterns: %s" % exp.pattern) if len(cls.statements) < len(cls.__actions): raise SystemExit("Unable to map statements to action") cls.ready = True Log.debug("All statements are scanned")
def print_report(self): """Dumps a report of the script outcome. """ Log.info("Preparing to print report...") Report.output()
def parse(self): """Parse guide and extract topics. User prompter asks to choose a topic from the selection in order to know what steps are queued. """ rr = ReadmeReader(validate=True) rr.read() rr.parse() Log.info("Parsing guide...") # Preview scanned guide if self.args.preview: Log.info("Loading guide preview...") rr.preview(150) # Scan guide for topics if self.args.topic is None: guide = rr.get_guide() else: guide = rr.get_guide_by_topic() if guide is None: Log.fatal("Guide has no such topic") # Prepare guide topics self.guide_topics = guide.get_topics() # Pair each step with appropriate statement if self.args.strict: if not all([self.pair_steps(t) for t in self.guide_topics]): Report.set_status("Halted") Report.set_error("Unsupported steps") Log.fatal("Cannot continue because of unsupported steps") else: for t in self.guide_topics: for step in t.get_steps(): pair = Matcher.pair_one(step) if not pair: self.guide_topics.remove(t) # Scan topics topics = [t.get_title() for t in guide.get_topics()] topics_len = len(topics) # Handle no topics if topics_len == 0: Log.fatal("No topics found") else: Log.info("Guide has %d topics" % topics_len) # Handle topics Log.info("Found the following topics...") print("") for pos, name in enumerate(topics): print(("%" + str(len(str(topics_len)) + 5) + "d) %s") % (pos + 1, name)) print(RUN_INSTRUCTIONS) # Display warnings if self.args.unsafe_shell: print(WARNING_UNSAFE_SHELL) # Save topic and topic's steps self.topic = self.get_user_input() self.steps = self.topic.get_steps() # Confirm topic print("") Log.info(u"Matching topic \033[92m%s\033[0m" % self.topic.get_title()) # Patch topic imports self.patch_topic_invoke(self.steps) # Prepare report Report.set_total_steps(len(self.steps)) Report.set_topic(self.topic.get_title()) return self
def launch_topic(self, ignore_fails=False, failed=False): """Launch topic and run all steps. """ total_steps = len(self.topic.get_steps()) Log.info("Preparing to run %d steps from topic..." % total_steps) # Loop steps and run each one start_time = default_timer() Log.debug("Setting start time: %s" % start_time) success_log = u"\033[92m\u2713 (Success)\033[0m \033[93m%s\033[0m" failed_log = u"\033[91m? (Failed)\033[0m \033[95m%s\033[0m" error_log = u"\033[91m? (Error) %s\033[0m" for self.last_step, step in enumerate(self.steps): step_count, step_desc = self.last_step + 1, step.get_description() Log.debug("Preparing step (%d) %s" % (step_count, step_desc)) Log.info(u"Running \033[93m%s\033[0m ..." % step.get_step()) try: success, output = step.run() if success: Log.info(success_log % output) else: Log.info(failed_log % output) if not ignore_fails: failed = True Report.set_error(output) break Report.inc_step(1) except KeyboardInterrupt: Log.info("Stopping current step...") Log.warn("Interrupting may lead to unexpected results") Log.warn("Stop master process at your own risk (PID %s)" % PID) except Exception as e: failed = True Report.set_error(e) Log.info(error_log % str(e)) break stop_time = default_timer() Log.debug("Set stop time: %s" % stop_time) Log.debug("Runtime %ss" % (stop_time - start_time)) # Save runtime Report.set_runtime(stop_time - start_time) # Wrap up... Log.debug("Done running steps...") title = self.topic.get_title() if failed: Log.info("An error occured while topic '%s' was running" % title) Report.set_status("Failed") else: Log.info("Topic '%s' has ran all steps with no errors" % title) Report.set_status("OK") Log.debug("Closing...")
def main(error=None): # Parse command line args args = Shell.parse() # System setup Sysenv.setup(__version__, args) # Check version and exit if args.version: return Sysenv.print_version() # Output application intro headers Sysenv.print_headers() # Set log verbose level Log.configure(verbose=args.verbose) # Set console verbose level Console.verbose = args.verbose # Prepare all statements Statement.prepare() # Lookup statement and example usage if args.lookup is not None: return scan_lookup(Statement, args.lookup) # Run analysis and exit if args.analyze: return analyze(Statement) # Self-analyze buildok and continue or crash self_analyze(None, Statement) or crash("Run an analyze and fix problems") # Setup placeholders Placeholder.config(args.placeholder) # Attach system environment to all actions Action.set_env(Sysenv) # Initialize script and run all steps guide_script = Script(args) guide_script.setup() # Create a lock file lock() try: # Parse guide and run all steps guide_script.parse().run() except Exception as e: if str(e) != "": error = e # Free lock file unlock() # Display report guide_script.print_report() # Throw any errors found... if environ.get("DEBUG") is not None and error is not None: raise Console.fatal(error)