summaryrefslogtreecommitdiff
path: root/update_commited_patches.py
blob: 2cd533158328edf3874bd1f23c65e0f984016f4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/python

import fcntl
import logging
import os
import operator
import sys

from bin import django_setup, add_logging_arguments

django_setup()  # must be called to get sys.path and django settings in place

from patchwork.models import Project, State

import gitrepo
import patch_matcher
from importlib import import_module

log = logging.getLogger('update_commited_patches')

_here = os.path.abspath(os.path.dirname(__file__))


def _assert_repo_dir(path):
    if not os.path.exists(path):
        os.mkdir(path)


def _update_commit(project, repo, commit, dryrun):
    patches = patch_matcher.get_patches_matching_commit(project, repo, commit)
    accepted = State.objects.get(name='Accepted')
    superseded = State.objects.get(name='Superseded')

    # sort the patches newest to oldest. the most recent gets marked as
    # accepted and everything else is superseded.
    patches = sorted(patches, key=operator.attrgetter('date'), reverse=True)
    for i, patch in enumerate(patches):
        if i == 0:
            patch.state = accepted
            patch.commit_ref = commit.id
        else:
            patch.state = superseded
        log.info('Updating patch %s, commit: %s, state: %s',
                 patch, commit.id, patch.state.name)
        if not dryrun:
            patch.save()


def _update_project(cb, repo_dir, project, commits, dryrun):
    log.info('Checking for updates to %s', project.linkname)

    repo = gitrepo.Repo(repo_dir, project.linkname, project.scm_url)
    repo.update()

    if commits:
        commits = [repo[x] for x in commits]
    else:
        save_state = dryrun is False
        commits = repo.process_unchecked_commits(save_state)

    for commit in commits:
        log.debug('check commit: %s', commit.id)
        try:
            _update_commit(project, repo, commit, dryrun)
            if cb:
                cb(project, repo, commit, dryrun)
        except MemoryError as e:
            log.error('Unable to process commit(%s): %s', commit.id, e)


def get_commit_callback_constructor():
    p = getattr(settings, 'UPDATE_COMMIT_CALLBACK', None)
    if p:
        module, func = p.rsplit(':', 1)
        module = import_module(module)
        return getattr(module, func)


if __name__ == '__main__':
    import argparse
    from django.conf import settings

    parser = argparse.ArgumentParser(
        description='Find patches that have been committed upstream')
    parser.add_argument('--dryrun', action='store_true',
                        help='Run through without changing anything in the DB')
    add_logging_arguments(parser)
    parser.add_argument('project', nargs='?',
                        help='only check on specific project')
    parser.add_argument('commit_id', nargs='*',
                        help='only check on given commit(s) for a project')
    args = parser.parse_args()

    _assert_repo_dir(settings.REPO_DIR)

    # Ensure no other copy of this script is running
    f = open(os.path.join(settings.REPO_DIR, '.lock'), 'w+')
    try:
        fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError:
        sys.exit('Script is already running')

    if args.project:
        projects = [Project.objects.get(linkname=args.project)]
    else:
        projects = Project.objects.filter(
            scm_url__isnull=False).exclude(scm_url='')

    cb_constructor = get_commit_callback_constructor()
    if cb_constructor:
        with cb_constructor() as cb:
            for p in projects:
                try:
                    _update_project(
                        cb, settings.REPO_DIR, p, args.commit_id, args.dryrun
                    )
                except:
                    log.exception('Error updating commits for: %s', p)

    if not cb_constructor:
        for p in projects:
            try:
                _update_project(
                    None, settings.REPO_DIR, p, args.commit_id, args.dryrun
                )
            except:
                log.exception('Error updating commits for: %s', p)