예제 #1
0
    def get(self, name, start=False, cls=Block):
        full_name = name
        #print('get', full_name)
        name, num = self._parse_name(name)
        if name in self:
            if start:
                assert num and num > 1

            block = self[name]
            if num:
                if start:
                    if num < 2:
                        raise KeyError('{0} is never an existing item'.format(full_name))
                    if num - 1 != len(block):
                        print(block)
                        raise KeyError('{0} while len is {1}'.format(full_name, len(block)))
                # foo.<id> should only be used sequentially
                assert len(block) == num - 1

                if block.name != self.last.name:
                    raise ParseError('unexpected block {0} after {1}'.format(
                        block.name, self.last.name))
                assert block == self.last  # this is the same as above
            return block
        else:
            if num:
                assert num == 1
            new_block = cls(name)
            self[name] = new_block
            return new_block
예제 #2
0
    def append_line(self, line):
        #print('appending', self, line)
        if self.finished():
            raise ParseError('block {0} is finished'.format(self))

        if remove_ansi_color(line) == '':
            self.elements.append(BlankLine())
        else:
            self.elements[0].append_line(line)
예제 #3
0
    def set_parameters(self, parameters):
        if not self.allow_empty:
            if not self._lines:
                raise ParseError('{0} is empty at end of parse'.format(self))

        parameters = dict(
            parameter.split('=') for parameter in parameters.split(','))
        for key, value in parameters.items():
            setattr(self, key, int(value))
예제 #4
0
 def append_line(self, line):
     if self.finished():
         raise ParseError('Unexpectedly adding {0} to {1}'.format(line, self))
     #print('adding {0} to {1}'.format(line, self))
     nocolor_line = remove_ansi_color(line)
     if nocolor_line.startswith('$ '):
         if self.elements and self._single_line_response:
             last_command = self.elements[-1]
             assert len(last_command.lines) == 2
         command = UntimedCommand()
         command.append_line(line)
         self.elements.append(command)
     else:
         if not len(self):
             raise ParseError('Unexpected version line: {0}'.format(line))
         last_command = self.elements[-1]
         if self._single_line_response:
             assert len(last_command.lines) == 1
         last_command.append_line(line)
예제 #5
0
    def append_line(self, line):
        if self.elements:
            last_command = self.elements[-1]
            #print('append to block', last_command, type(last_command), line, last_command.finished())
            if isinstance(last_command, TimedCommand):
                raise ParseError('not expecting {0}.append_line({1})'.format(self, line))

        nocolor_line = remove_ansi_color(line)
        if nocolor_line.startswith('$ '):
            new_command = UntimedCommand()
            new_command.append_line(line)
        elif self.elements:
            last_command = self.elements[-1]
            last_command.append_line(line)
        else:
            new_item = Note()
            new_item.append_line(line)
예제 #6
0
    def append_line(self, line):
        #print('CommandBlock.append_line ', line)
        nocolor_line = remove_ansi_color(line)

        assert self.commands

        last_command = self.commands[-1]
        if isinstance(last_command, BlankLine):
            last_command = self.commands[-2]

        if not isinstance(last_command, Command):
            if last_command and last_command.identifier in ['_unsolved_exit_code-1', '_unsolved_exit_code-2']:
                pass
            else:
                raise ParseError('last command is {0}: {1} when inserting: {2}'.format(type(last_command), last_command, line))

        if len(last_command.lines):

            # clean the last command line, and remove the '$ '
            exit_code_pattern = 'The command "{0}" exited with '.format(remove_ansi_color(last_command.lines[0])[2:])
            if nocolor_line.startswith(exit_code_pattern):
                exit_code = nocolor_line[len(exit_code_pattern):-1]
                last_command.exit_code = int(exit_code)
                return
            elif nocolor_line and exit_code_pattern.startswith(nocolor_line):
                # TODO: The exit_code_pattern needs to be a multi-line match
                # e.g. happy5214/pywikibot-core/6.10
                current_command = Note('_unsolved_exit_code-1')
                self.commands.append(current_command)
                return

            exit_code_pattern = 'The command "{0}" failed and exited with '.format(remove_ansi_color(last_command.lines[0])[2:])
            if nocolor_line.startswith(exit_code_pattern):
                exit_code = nocolor_line[len(exit_code_pattern):].split(' during ')[0]
                last_command.exit_code = int(exit_code)
                return

        last_command.append_line(line)
예제 #7
0
 def append_line(self, line):
     if remove_ansi_color(line) != '':
         raise ParseError('BlankLineBlock not expecting {0}'.format(line))
     self.elements.append(BlankLine())
예제 #8
0
 def append_line(self, line):
     assert len(self.elements) == 1
     if len(self.elements[0].lines) != 0:
         raise ParseError('cant insert line into {0}: {1}'.format(self, line))
     assert len(self.elements[0].lines) == 0
     self.elements[0].append_line(line)
예제 #9
0
    def _parse(self):
        if not self.body:
            return {}

        no_system_info = False

        if 'travis_fold:start:system_info' not in self.body[:400]:
            if len(self.body) < 400:
                lines = self.body.strip().splitlines()
                # remove blank lines
                lines = [line for line in lines if line]
                if lines[0].startswith('Using worker: '):
                    lines = lines[1:]
                if len(lines) == 1 and lines[0] == 'Done: Job Cancelled':
                    return {}
            else:
                if 'travis_fold:start:system_info' in self.body:
                    # See https://github.com/travis-ci/travis-ci/issues/4848
                    raise TravisLogCorrupt
                elif 'travis_fold:start:git' in self.body[:400]:
                    no_system_info = True
                else:
                    raise ParseError('header not found')

        blocks = BlockDict()

        lines = self.body.splitlines()

        if no_system_info:
            print('no_system_info!')

        current_block = None
        current_command = None
        successful = False

        for line_no, line in enumerate(lines):
            nocolor_line = remove_ansi_color(line)

            #print('line', current_block, current_command, nocolor_line)

            if nocolor_line.startswith('travis_time:start:'):
                timer_id = nocolor_line[len('travis_time:start:'):]
                assert timer_id

                current_block = blocks.last

                #print('adding start', current_block, current_block.finished(), timer_id)

                if current_block in ['_versions', '_versions-continued']:
                    blocks.append(ScriptBlock('script'))

                elif current_block and current_block.finished():
                    if (current_block.name.endswith('_environment_variables') or
                            current_block.name == '_container_notice' or
                            current_block.name.startswith('git')):
                        if 'system_info' in blocks:
                            build_language = blocks['system_info'].elements[0].lines[1]
                            build_language = build_language[len('Build language: '):]
                        else:
                            build_language = None
                        if build_language == 'php':
                            blocks.append(PHPActivateBlock('_activate'))
                        else:
                            blocks.append(SingleCommandBlock('_activate'))

                    elif current_block.name in '_activate':
                        blocks.append(CommandBlock('_versions-timed'))
                    elif current_block.name in ['before_script', 'install', 'install.bundler'] or current_block.name in ['before_script-continued', 'install-continued']:
                        blocks.append(ScriptBlock('script'))
                    else:
                        raise ParseError('unexpected line after {0}: {1}'.format(current_block, line))

                current_block = blocks.last

                #print('start', current_block)

                if current_block is None:
                    print(blocks)
                    raise ParseError('unexpected start: {0}'.format(line))

                allow_empty = current_block.allow_empty()

                current_command = TimedCommand(timer_id, allow_empty)
                current_block.append(current_command)

                #print('start timed inserted', current_block)

            elif nocolor_line.startswith('travis_time:end:'):
                data = nocolor_line[len('travis_time:end:'):]
                end_timer_id, parameters = data.split(':', 1)

                current_command = blocks.last.last_item

                if not isinstance(current_command, TimedCommand):
                    if isinstance(blocks.last, JobCancelled):
                        last_timed_command = blocks[-2].last_item
                        assert isinstance(last_timed_command, TimedCommand)
                        current_command = ContinuedTimedCommand(last_timed_command, allow_empty=True)
                    else:
                        raise ParseError('Last item is a {0} and not a TimedCommand: {1}'.format(type(current_command), current_command))

                #print('end', current_block, current_command, end_timer_id)
                if current_command.identifier != end_timer_id:
                    raise ParseError('{0} is not {1}'.format(current_command, end_timer_id))

                #if not current_command.lines:
                #    print('end with no lines', end_timer_id, current_block, current_command)
                #    sys.exit()

                current_command.set_parameters(parameters)
                current_command = None
            elif nocolor_line.startswith('travis_fold:start:'):
                block_name = nocolor_line[len('travis_fold:start:'):]

                last_block = blocks.last

                cls = None
                if no_system_info and block_name == 'git.1':
                    print('odd git block')
                    if blocks.last == '_top_env':
                        cls = AutoGitBlock
                    else:
                        cls = OldGitBlock
                elif block_name == 'apt':
                    cls = AptBlock
                elif block_name == 'system_info':
                    cls = OneNoteBlock
                elif block_name == 'announce':
                    cls = AutoCommandBlock
                elif block_name == 'before_install':
                    cls = MixedCommandBlock
                else:
                    cls = MixedCommandBlock

                current_block = blocks.get(block_name, start=True, cls=cls)

                # The number of git blocks is variable
                # If another one appears, it is OK.
                if current_block.name == 'git' and current_block._finished:
                    current_block._finished = False

                if last_block and current_block != last_block and last_block.finished() == False and last_block.name != 'git':
                    raise ParseError('start of {0} during {1} unexpected after {2} ({3})'.format(block_name, current_block, last_block, last_block.finished()))

                if no_system_info and block_name == 'git.3' and current_block.__class__ == OldGitBlock:
                    assert len(blocks) == 2  # block 0 should be _worker
                    assert len(current_block) > 1  # TODO: better assert

                    current_command = UntimedCommand('_untimed_git_checkout')
                    current_block.commands.append(current_command)

                current_block._finished = None

            elif nocolor_line.startswith('travis_fold:end:'):
                block_name = nocolor_line[len('travis_fold:end:'):]
                current_block = blocks[block_name]

                if not current_block == blocks.last:
                    if blocks.last.name == current_block.name + '-continued':
                        blocks.last._finished = True
                    elif isinstance(blocks.last, JobCancelled):  # not used
                        current_block = blocks[-2]
                        last_timed_command = blocks[-2].last_item
                        assert isinstance(last_timed_command, TimedCommand)
                    else:
                        raise ParseError('{0} != {1}'.format(current_block, blocks.last))

                if hasattr(current_block.last_item, '_finished'):
                    current_block.last_item._finished = True
                current_block._finished = True
                current_block = None
                current_command = None
                if block_name == 'announce':  # wikimedia/wikipedia-ios/543.1 needs this
                    blocks.append(AutoVersionCommandBlock('_versions-extra'))
            else:
                # ruby coveralls includes travis variables in its payload
                if 'travis_' in line and not '"travis_' in line:
                    raise ParseError(
                        'unexpected travis_ in {0} while parsing {1}'.format(
                            line, current_block))

                new_block = find_new_block(line)
                if new_block is not None:
                    #print('found new block', new_block)
                    #if blocks:
                    #    print('last block was', blocks.last)
                    blocks.append(new_block)
                    # Single line item?
                    if new_block.finished():
                        current_block = None

                    current_block = new_block
                    if current_block.elements:
                        current_command = current_block.elements[-1]
                        continue
                    else:
                        current_command = None

                #print(line)

                if no_system_info and blocks.last and blocks.last.name == '_worker' and blocks.last.finished():
                    # probably a cpp block, as most environments have a 'system_info' block
                    # which is handled above
                    blocks.append(AutoCommandBlock('_top_env'))

                if blocks and blocks.last.name in ['rvm'] and blocks.last.finished():
                    if 'jupiter' in blocks['_worker'].elements[0].lines[0]:
                        print('using OSXRubyVersionBlock')
                        current_block = OSXRubyVersionBlock('_versions-odd')
                    else:
                        current_block = AutoVersionCommandBlock('_versions')
                    blocks.append(current_block)
                elif current_command is None and current_block in ['git.checkout', 'git.submodule'] and nocolor_line.startswith('The command "git ') and '" failed and exited with ' in nocolor_line:
                    # TODO: match the command in the quotes
                    current_command = current_block.commands[-1]

                if current_block == '_activate' and current_block.finished():
                    print('after _activate')
                    blocks.append(AutoVersionCommandBlock('_versions'))
                    current_block = blocks.last

                if current_block == '_job_cancelled' and current_block.finished():
                    if isinstance(blocks[-2].elements[-1], TimedCommand):
                        current_block = blocks[-2].__class__(blocks[-2].name + '-continued')
                        current_command = ContinuedTimedCommand(blocks[-2].elements[-1], allow_empty=True)
                        current_block.append(current_command)
                    else:
                        current_block = Block('_stuff_after_job_cancelled-' + str(line_no))
                    blocks.append(current_block)

                current_block = blocks.last

                if current_block is not None:
                    #print(current_block.name, current_block.finished(), line)
                    if no_system_info and current_block == 'before_install' and current_block.finished():
                        blocks.append(AutoScriptBlock('script'))
                        current_block = blocks.last

                    if current_block.finished():
                        print('current block is finished', current_block)
                        if nocolor_line.startswith('$ ') and nocolor_line.endswith('-- version'):
                            blocks.append(AutoVersionCommandBlock('_versions'))
                            current_block = blocks.last
                        elif nocolor_line == '':
                            blocks.append(BlankLineBlock('_unexpected_blank_lines-' + str(line_no)))
                            continue
                    if 'Done' in line:
                        print('adding {0} to block'.format(line))
                    try:
                        current_block.append_line(line)
                    except Exception:
                        print('failed on line {0}: {1}'.format(line_no, line))
                        raise
                elif nocolor_line:  # ignore blank lines
                    previous_block_name = None if not blocks else blocks[-1].name
                    raise ParseError('unexpected line after {0}: {1!r}'.format(previous_block_name, line))

            for block in blocks:
                if block.__class__ == Block:
                    raise ParseError('Block needs to be converted to something else')

            if blocks.last == '_done':
                done_block = blocks.last
                assert done_block.exit_code is not None
                previous_block = blocks[-2]
                if previous_block.name.startswith('_unexpected_blank_lines'):
                    previous_block = blocks[-3]
                    if previous_block.name == 'after_success':  # wikimedia/wikipedia-ios/543.1
                        previous_block = blocks[-4]
                assert previous_block.name in ['script', 'script-continued']
                last_command = previous_block.commands[-1]
                assert isinstance(last_command, Command)
                if last_command.exit_code is None:
                    pass
                    #raise ParseError('Build exit with {0}, but last command {1} doesnt have an exit code'.format(done_block.exit_code, last_command))
                elif done_block.exit_code > 0 and last_command.exit_code == 0:
                    print('Build exit with {0}, but last command exit code was {1}'.format(done_block.exit_code, last_command.exit_code))
                elif done_block.exit_code > 0 and last_command.exit_code == 0:
                    raise ParseError('Build exit with {0}, but last command exit code was {1}'.format(done_block.exit_code, last_command.exit_code))
                continue

        return blocks
예제 #10
0
 def append(self, block):
     if block.name in self:
         raise ParseError('{0} already in {1}'.format(block, self))
     self[block.name] = block