Statistics
| Branch: | Tag: | Revision:

root / devflow / flow.py @ e278c46e

History | View | Annotate | Download (7.2 kB)

1
import os
2

    
3
import logging
4
logging.basicConfig()
5
from optparse import OptionParser
6

    
7
os.environ["GIT_PYTHON_TRACE"] = "full"
8
from devflow import utils, versioning
9
from devflow.version import __version__
10
from devflow.autopkg import call
11
from functools import wraps
12
from contextlib import contextmanager
13
from git.exc import GitCommandError
14

    
15

    
16
def cleanup(func):
17
    @wraps(func)
18
    def wrapper(self, *args, **kwargs):
19
        try:
20
            return func(self, *args, **kwargs)
21
        except:
22
            self.log.debug("Unexpected ERROR. Cleaning up repository...")
23
            self.repo.git.reset("--hard", "HEAD")
24
            self.repo.git.checkout(self.start_branch)
25
            self.repo.git.reset("--hard", self.start_hex)
26
            for branch in self.new_branches:
27
                self.repo.git.branch("-D", branch)
28
            for tag in self.new_tags:
29
                self.repo.git.tag("-D", tag)
30
            raise
31
    return wrapper
32

    
33

    
34
@contextmanager
35
def conflicts():
36
    try:
37
        yield
38
    except GitCommandError as e:
39
        if e.status != 128:
40
            print "An error occured. Resolve it and type 'exit'"
41
            call("bash")
42
        else:
43
            raise
44

    
45

    
46
class GitManager(object):
47
    def __init__(self):
48
        self.repo = utils.get_repository()
49
        self.start_branch = self.repo.active_branch.name
50
        self.start_hex = self.repo.head.log()[-1].newhexsha
51
        self.log = logging.getLogger("")
52
        self.log.setLevel(logging.DEBUG)
53
        self.log.info("Repository: %s. HEAD: %s", self.repo, self.start_hex)
54
        self.new_branches = []
55
        self.new_tags = []
56
        self.repo.git.pull("origin")
57

    
58
    def get_branch(self, mode, version):
59
        if mode not in ["release", "hotfix"]:
60
            raise ValueError("Unknown mode: %s" % mode)
61
        return "%s-%s" % (mode, version)
62

    
63
    def get_debian_branch(self, mode, version):
64
        if mode not in ["release", "hotfix"]:
65
            raise ValueError("Unknown mode: %s" % mode)
66
        return "debian-%s-%s" % (mode, version)
67

    
68
    @cleanup
69
    def start_release(self, version):
70
        self.start_common("release", version)
71

    
72
    @cleanup
73
    def start_hotfix(self, version):
74
        self.start_common("hotfix", version)
75

    
76
    @cleanup
77
    def end_release(self, version):
78
        repo = self.repo
79
        master = "master"
80
        debian_master = "debian"
81
        upstream = "develop"
82
        debian = "debian-develop"
83
        upstream_branch = self.get_branch("release", version)
84
        debian_branch = self.get_debian_branch("release", version)
85
        repo.git.checkout(upstream)
86
        with conflicts():
87
            repo.git.merge("--no-ff", upstream_branch)
88
        repo.git.checkout(debian)
89
        with conflicts():
90
            repo.git.merge("--no-ff", debian_branch)
91

    
92
        repo.git.checkout(master)
93
        with conflicts():
94
            repo.git.merge("--no-ff", upstream_branch)
95
        repo.git.checkout(debian_master)
96
        with conflicts():
97
            repo.git.merge("--no-ff", debian_branch)
98

    
99
        repo.git.checkout(upstream)
100
        print "To remove obsolete branches run:"
101
        print "git branch -d %s" % upstream_branch
102
        print "git branch -d %s" % debian_branch
103

    
104
    @cleanup
105
    def end_hotfix(self, version):
106
        repo = self.repo
107
        upstream = "master"
108
        debian = "debian"
109
        upstream_branch = self.get_branch("hotfix", version)
110
        debian_branch = self.get_debian_branch("hotfix", version)
111

    
112
        repo.git.checkout(upstream)
113
        with conflicts():
114
            repo.git.merge("--no-ff", upstream_branch)
115
        repo.git.checkout(debian)
116
        with conflicts():
117
            repo.git.merge("--no-ff", debian_branch)
118

    
119
        repo.git.checkout(upstream)
120
        print "To remove obsolete branches run:"
121
        print "git branch -d %s" % upstream_branch
122
        print "git branch -d %s" % debian_branch
123

    
124
    def start_common(self, mode, version):
125
        if mode not in ["release", "hotfix"]:
126
            raise ValueError("Unknown mode: %s" % mode)
127
        repo = self.repo
128
        upstream = "develop" if mode == "release" else "master"
129
        debian = "debian-develop" if mode == "release" else "debian"
130
        upstream_branch = "%s-%s" % (mode, version)
131
        debian_branch = "debian-%s-%s" % (mode, version)
132
        repo.git.checkout(upstream)
133
        repo.git.branch(upstream_branch, upstream)
134
        self.new_branches.append(upstream_branch)
135
        versioning.bump_version("%snext" % version)
136
        repo.git.checkout(upstream_branch)
137
        versioning.bump_version("%src1" % version)
138
        repo.git.checkout(debian)
139
        repo.git.branch(debian_branch, debian)
140
        self.new_branches.append(debian_branch)
141
        repo.git.checkout(upstream_branch)
142
        repo.git.checkout(debian)
143

    
144
    @cleanup
145
    def start_feature(self, feature_name):
146
        repo = self.repo
147
        feature_upstream = "feature-%s" % feature_name
148
        feature_debian = "debian-%s" % feature_upstream
149
        repo.git.branch(feature_upstream, "develop")
150
        self.new_branches.append(feature_upstream)
151
        repo.git.branch(feature_debian, "debian-develop")
152
        self.new_branches.append(feature_debian)
153

    
154
    @cleanup
155
    def end_feature(self, feature_name):
156
        repo = self.repo
157
        feature_upstream = "feature-%s" % feature_name
158
        if not feature_upstream in repo.branches:
159
            raise ValueError("Branch %s does not exist." % feature_upstream)
160
        feature_debian = "debian-%s" % feature_upstream
161
        self.edit_changelog(feature_upstream)
162
        repo.git.checkout("develop")
163
        with conflicts():
164
            repo.git.merge(feature_upstream)
165
        repo.git.checkout("debian-develop")
166
        if feature_debian in repo.branches:
167
            with conflicts():
168
                repo.git.merge(feature_debian)
169
        repo.git.checkout("develop")
170
        print "To remove obsolete branches run:"
171
        print "git branch -D %s" % feature_upstream
172
        if feature_debian in repo.branches:
173
            print "git branch -D %s" % feature_debian
174

    
175
    def edit_changelog(self, branch):
176
        repo = self.repo
177
        if not branch in repo.branches:
178
            raise ValueError("Branch %s does not exist." % branch)
179

    
180
        repo.git.checkout(branch)
181
        topdir = repo.working_dir
182
        changelog = os.path.join(topdir, "Changelog")
183
        editor = os.getenv('EDITOR')
184
        if not editor:
185
            editor = 'vim'
186
        call("%s %s" % (editor, changelog))
187
        repo.git.add(changelog)
188
        repo.git.commit(m="Update changelog")
189
        print "Updated changelog on branch %s" % branch
190

    
191
    def end_common(self, mode, version):
192
        pass
193

    
194

    
195
def refhead(repo):
196
    return repo.head.log[-1].newhexsha
197

    
198

    
199
def main():
200
    HELP_MSG = "usage: %prog mode=[release|hotfix|feature]"\
201
               " [version|name]"
202
    parser = OptionParser(usage=HELP_MSG,
203
                          version="devflow-flow %s" % __version__,
204
                          add_help_option=True)
205
    options, args = parser.parse_args()
206
    if len(args) != 3:
207
        parser.error("Invalid number of arguments.")
208
    mode, action, version = args
209
    gm = GitManager()
210
    func = "%s_%s" % (action, mode)
211
    try:
212
        getattr(gm, func)(version)
213
    except AttributeError:
214
        parser.error("Invalid arguments.")
215

    
216
if __name__ == "__main__":
217
    main()