Exemple #1
0
class PatchRecursiveMission(object):
    OLD_DIR = os.path.join(get_mission_data_path('diffpatch'), 'recipes')
    NEW_DIR = os.path.join(
        get_mission_data_path('diffpatch'), 'recipes.more-garlic')
    BASE_NAME = 'recipes'
    ANSWERS = {'amount_of_garlic': 3}

    # NOTE: You should rely on the DiffRecursiveMission for the tarball.

    @classmethod
    def get_patch(cls):
        patchfile = StringIO()
        for name in os.listdir(cls.OLD_DIR):
            oldname = os.path.join(cls.OLD_DIR, name)
            newname = os.path.join(cls.NEW_DIR, name)
            if not os.path.isfile(oldname):
                continue

            oldlines = open(oldname).readlines()
            newlines = open(newname).readlines()
            patchfile.writelines(
                difflib.unified_diff(oldlines, newlines, 'a/%s/%s' %
                                     (cls.BASE_NAME, name), 'b/%s/%s' % (cls.BASE_NAME, name)))

        return patchfile.getvalue()
class PatchSingleFileMission(SingleFilePatch):
    OLD_FILE = os.path.join(get_mission_data_path('diffpatch'),
                            'oven-pancake.txt')
    NEW_FILE = os.path.join(get_mission_data_path('diffpatch'),
                            'oven-pancake_result.txt')
    # This does not correspond to a real file but is merely the filename the download is presented as.
    PATCH_FILENAME = 'add-oven-temp.patch'
Exemple #3
0
 def synthesize_tarball(cls):
     tdata = StringIO()
     tfile = tarfile.open(fileobj=tdata, mode='w:gz')
     tfile.add(os.path.join(get_mission_data_path('diffpatch'),
               cls.ORIG_DIR), cls.ORIG_DIR)
     tfile.close()
     return tdata.getvalue()
Exemple #4
0
    def validate_patch(cls, patchdata):
        the_patch = patch.fromstring(patchdata)

        # Strip one level of directories from the left of the filenames.
        for i, filename in enumerate(the_patch.source):
            cls.check_for_leading_dot_slash_in_filename(filename)
            the_patch.source[i] = cls.strip_filename_one_path_level(filename)
        for i, filename in enumerate(the_patch.target):
            cls.check_for_leading_dot_slash_in_filename(filename)
            the_patch.target[i] = cls.strip_filename_one_path_level(filename)

        # Go through the files and check that ones that should be mentioned in
        # the patch are so mentioned.
        path_to_mission_files = os.path.join(
            get_mission_data_path('diffpatch'), cls.ORIG_DIR)
        for filename in os.listdir(path_to_mission_files):
            old_style_filename = filename
            full_filename = os.path.join(path_to_mission_files, filename)
            if not os.path.isfile(full_filename):
                continue

            old_contents = open(full_filename).read()
            new_contents = old_contents
            for old, new in cls.SUBSTITUTIONS:
                new_contents = new_contents.replace(old, new)
            if old_contents == new_contents:
                continue

            # So it's a file that the patch should modify.
            try:
                index = the_patch.source.index(filename)
            except ValueError:
                # A file the user was supposed to modify was not included in
                # the patch.
                #
                # We did rename a bunch of the files a little while ago.
                # Maybe we can process their submission anyway by looking for
                # the old filename in the patch header.
                old_style_filename = DiffRecursiveMission.name_new2old(
                    filename)
                try:
                    index = the_patch.source.index(old_style_filename)
                except ValueError:
                    raise IncorrectPatch, 'Patch does not modify file "%s", which it should modify.' % filename

            if (the_patch.target[index] != filename and
                    the_patch.target[index] != old_style_filename):
                raise IncorrectPatch, 'Patch headers for file "%s" have inconsistent filenames.' % filename

            hunks = the_patch.hunks[index]
            del the_patch.source[index]
            del the_patch.target[index]
            del the_patch.hunks[index]
            del the_patch.hunkends[index]

            # Check that it will apply correctly to the file.
            if not the_patch._match_file_hunks(full_filename, hunks):

                # Check for reverse patch by seeing if there is "-firstSubstitute" and "+firstOriginal" in the diff.
                # (If they did everything perfectly and reversed the patch, there will be two lines following these conditions)
                if patchdata.find('-' + cls.SUBSTITUTIONS[1][1]) != -1 and patchdata.find('+' + cls.SUBSTITUTIONS[1][0]) != -1:
                    raise IncorrectPatch, 'You submitted a patch that would revert the correct changes back to the originals.  You may have mixed the parameters for diff, or performed a reverse patch.'
                else:
                    raise IncorrectPatch, 'The modifications to "%s" will not apply correctly to the original file.' % filename

            # Check for BOM issues.  Likely a Windows-only issue, and only when using a text editor that
            # includes UTF-8 BOM markers when saving files.
            if '\xef\xbb\xbf' in ''.join(the_patch.patch_stream(StringIO(old_contents), hunks)):
                raise IncorrectPatch, 'It appears the text editor you used to modify "%s" leaves UTF-8 BOM characters.  Try an editor like Notepad++ or something similar.' % filename

            # Check that the resulting file matches what is expected.
            if ''.join(the_patch.patch_stream(StringIO(old_contents), hunks)) != new_contents:
                raise IncorrectPatch, 'The modifications to "%s" do not result in the correct contents. Make sure you replaced "Aubergine", too!' % filename

        if len(the_patch.source) != 0:
            raise IncorrectPatch, 'The patch modifies files that it should not modify: %s' % ', '.join(
                the_patch.source)
Exemple #5
0
class DiffSingleFileMission(SingleFilePatch):
    OLD_FILE = os.path.join(
        get_mission_data_path('diffpatch'), 'nutty-pancake.txt')
    NEW_FILE = os.path.join(
        get_mission_data_path('diffpatch'), 'nutty-pancake_result.txt')
Exemple #6
0
class DiffForm(django.forms.Form):
    diff = django.forms.CharField(
        error_messages={'required': 'No svn diff output was given.'},
        widget=django.forms.Textarea())
    FILE_TO_BE_PATCHED = 'README'
    NEW_CONTENT = os.path.join(get_mission_data_path('svn'),
                               'README-new-for-svn-diff')

    def __init__(self,
                 username=None,
                 wcdir=None,
                 request=None,
                 *args,
                 **kwargs):
        super(DiffForm, self).__init__(request, *args, **kwargs)
        self.username = username
        self.wcdir = wcdir
        if wcdir:
            self.file_to_patch = os.path.join(wcdir, self.FILE_TO_BE_PATCHED)
        self.the_patch = None
        self.new_content = None

    def clean_diff(self):
        """
        Validate the diff form.
        This function will be invoked by django.form.Forms.is_valid(), and
        will raise the exception ValidationError
        """
        self.the_patch = patch.fromstring(self.cleaned_data['diff'])
        # Check that the submitted diff patches the correct number of files
        if len(self.the_patch.hunks) != 1:
            raise ValidationError, 'The patch affects more than one file.'

        # Check that the filename it patches is correct.
        if self.FILE_TO_BE_PATCHED not in self.cleaned_data['diff']:
            raise ValidationError, 'The patch affects the wrong file.'

        # Now we need to generate a working copy to apply the patch to.
        # We can also use this working copy to commit the patch if it's OK.
        repo = view_helpers.SvnRepository(self.username)
        view_helpers.subproc_check_output(
            ['svn', 'co', repo.file_trunk_url(), self.wcdir])

        # Check that it will apply correctly to the working copy.
        if not self.the_patch._match_file_hunks(self.file_to_patch,
                                                self.the_patch.hunks[0]):
            raise ValidationError, 'The patch will not apply correctly to the lastest revision.'

        # Check that the resulting file matches what is expected.
        self.new_content = ''.join(
            self.the_patch.patch_stream(open(self.file_to_patch),
                                        self.the_patch.hunks[0]))
        if self.new_content != open(self.NEW_CONTENT).read():
            raise ValidationError, 'The file resulting from patching does not have the correct contents.'

    def commit_diff(self):
        """Commit the diff form and the patch."""
        open(self.file_to_patch, 'w').write(self.new_content)
        commit_message = '''Fix a typo in %s. Thanks for reporting this, %s!''' % (
            self.FILE_TO_BE_PATCHED, self.username)
        view_helpers.subproc_check_output([
            'svn', 'commit', '-m', commit_message, '--username', 'mr_bad',
            self.wcdir
        ])