~ubuntu-archive/ubuntu-archive-tools/trunk

1395 by Łukasz 'sil2100' Zemczak
Make branch-seeds python3
1
#! /usr/bin/python3
407 by Colin Watson
add new branch-seeds script to branch a set of seed collections when opening a new series
2
3
# Copyright (C) 2012  Canonical Ltd.
4
# Author: Colin Watson <cjwatson@ubuntu.com>
5
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; version 3 of the License.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18
"""Branch a set of Ubuntu seeds for the next release."""
19
20
from __future__ import print_function
21
22
from optparse import OptionParser
23
import os
24
import re
1314 by Łukasz 'sil2100' Zemczak
Make branch-seeds wait a bit for the new git refs to appear before attempting to set the default_branch
25
import time
407 by Colin Watson
add new branch-seeds script to branch a set of seed collections when opening a new series
26
import subprocess
27
try:
28
    from urllib.parse import urlparse
29
except ImportError:
30
    from urlparse import urlparse
31
32
from launchpadlib.launchpad import Launchpad
1142.1.2 by Simon Quigley
Refactor some changes after feedback from slangasek.
33
from enum import Enum
34
35
class VCS(Enum):
36
    Git = 1
37
    Bazaar = 2
38
39
    @staticmethod
40
    def detect_vcs(source):
41
        if os.path.exists(os.path.join(source, ".git")):
42
            return VCS.Git
43
        elif os.path.exists(os.path.join(source, ".bzr")):
44
            return VCS.Bazaar
45
        else:
46
            return None
1142.1.1 by Simon Quigley
Add Git support to branch-seeds.
47
48
1143.1.2 by Simon Quigley
Split remote_branch into two different functions dependent on the VCS.
49
def remote_bzr_branch(source):
50
    # TODO: should really use bzrlib instead
51
    info = subprocess.check_output(
52
        ["bzr", "info", source], universal_newlines=True)
53
    for line in info.splitlines():
54
        if "checkout of branch:" in line:
55
            return line.split(": ")[1].rstrip("/")
56
    else:
57
        raise Exception("Unable to find remote branch for %s" % source)
58
1150 by Colin Watson
branch-seeds: whitespace nits
59
1143.1.4 by Simon Quigley
Add additional code tweaks.
60
def remote_git_repository(source, srcbranch):
1143.1.3 by Simon Quigley
Make adjustments from Colin's suggestions.
61
    fullbranch = subprocess.check_output(
1150 by Colin Watson
branch-seeds: whitespace nits
62
        ["git", "rev-parse", "--symbolic-full-name",
63
         srcbranch + "@{upstream}"],
1143.1.3 by Simon Quigley
Make adjustments from Colin's suggestions.
64
        universal_newlines=True, cwd=source)
65
    return subprocess.check_output(
66
        ["git", "ls-remote", "--get-url", fullbranch.split("/")[2]],
1149 by Colin Watson
branch-seeds: move rstrip("\n") to a clearer place
67
        universal_newlines=True, cwd=source).rstrip("\n")
407 by Colin Watson
add new branch-seeds script to branch a set of seed collections when opening a new series
68
69
70
def lp_branch(options, url):
71
    return options.launchpad.branches.getByUniqueName(
72
        unique_name=urlparse(url).path.lstrip("/"))
73
74
75
def branch(options, collection):
76
    source = "%s.%s" % (collection, options.source_series)
77
    dest = "%s.%s" % (collection, options.dest_series)
1142.1.2 by Simon Quigley
Refactor some changes after feedback from slangasek.
78
    vcs = VCS.detect_vcs(source)
1142.1.3 by Simon Quigley
Reduce to if vcs, rest of that if/else isn't needed.
79
    if vcs:
1142.1.2 by Simon Quigley
Refactor some changes after feedback from slangasek.
80
        if vcs is VCS.Bazaar:
1142.1.1 by Simon Quigley
Add Git support to branch-seeds.
81
            subprocess.check_call(["bzr", "up", source])
1143.1.2 by Simon Quigley
Split remote_branch into two different functions dependent on the VCS.
82
            remote_source = remote_bzr_branch(source)
1142.1.1 by Simon Quigley
Add Git support to branch-seeds.
83
            remote_dest = os.path.join(os.path.dirname(remote_source), dest)
84
            subprocess.check_call(["bzr", "branch", source, dest])
85
            subprocess.check_call(["bzr", "push", "-d", dest, remote_dest])
86
            subprocess.check_call(["bzr", "bind", ":push"], cwd=dest)
87
88
            lp_source = lp_branch(options, remote_source)
89
            lp_source.lifecycle_status = "Mature"
90
            lp_source.lp_save()
91
92
            lp_dest = lp_branch(options, remote_dest)
93
            lp_dest.lifecycle_status = "Development"
94
            lp_dest.lp_save()
1142.1.2 by Simon Quigley
Refactor some changes after feedback from slangasek.
95
        elif vcs is VCS.Git:
1142.1.1 by Simon Quigley
Add Git support to branch-seeds.
96
            subprocess.check_call(["git", "fetch"], cwd=source)
1142.1.2 by Simon Quigley
Refactor some changes after feedback from slangasek.
97
            subprocess.check_call(["git", "reset", "--hard", "FETCH_HEAD"], cwd=source)
1142.1.1 by Simon Quigley
Add Git support to branch-seeds.
98
            os.rename(source, dest)
99
            subprocess.check_call(["git", "checkout", "-b", options.dest_series], cwd=dest)
407 by Colin Watson
add new branch-seeds script to branch a set of seed collections when opening a new series
100
101
        re_include_source = re.compile(
102
            r"^(include )(.*)\.%s" % options.source_series)
103
        new_lines = []
104
        message = []
105
        with open(os.path.join(dest, "STRUCTURE")) as structure:
106
            for line in structure:
107
                match = re_include_source.match(line)
108
                if match:
109
                    new_lines.append(re_include_source.sub(
110
                        r"\1\2.%s" % options.dest_series, line))
111
                    message.append(
112
                        "%s.%s -> %s.%s" %
113
                        (match.group(2), options.source_series,
114
                         match.group(2), options.dest_series))
115
                else:
116
                    new_lines.append(line)
117
        if message:
118
            with open(os.path.join(dest, "STRUCTURE.new"), "w") as structure:
119
                for line in new_lines:
120
                    print(line, end="", file=structure)
121
            os.rename(
122
                os.path.join(dest, "STRUCTURE.new"),
123
                os.path.join(dest, "STRUCTURE"))
1142.1.2 by Simon Quigley
Refactor some changes after feedback from slangasek.
124
            if vcs is VCS.Bazaar:
1142.1.1 by Simon Quigley
Add Git support to branch-seeds.
125
                subprocess.check_call(
126
                    ["bzr", "commit", "-m", "; ".join(message)], cwd=dest)
1142.1.2 by Simon Quigley
Refactor some changes after feedback from slangasek.
127
            elif vcs is VCS.Git:
1142.1.1 by Simon Quigley
Add Git support to branch-seeds.
128
                subprocess.check_call(["git", "add", "STRUCTURE"], cwd=dest)
129
                subprocess.check_call(
1142.1.2 by Simon Quigley
Refactor some changes after feedback from slangasek.
130
                    ["git", "commit", "-m", "; ".join(message)], cwd=dest)
1142.1.1 by Simon Quigley
Add Git support to branch-seeds.
131
                subprocess.check_call(
132
                    ["git", "push", "origin", options.dest_series], cwd=dest)
407 by Colin Watson
add new branch-seeds script to branch a set of seed collections when opening a new series
133
1143.1.4 by Simon Quigley
Add additional code tweaks.
134
                remote = remote_git_repository(dest, options.source_series)
1143.1.3 by Simon Quigley
Make adjustments from Colin's suggestions.
135
                if "git.launchpad.net" in remote:
136
                    lp_git_repo = options.launchpad.git_repositories.getByPath(
1150 by Colin Watson
branch-seeds: whitespace nits
137
                        path=urlparse(remote).path.lstrip("/"))
1314 by Łukasz 'sil2100' Zemczak
Make branch-seeds wait a bit for the new git refs to appear before attempting to set the default_branch
138
                    new_ref = "refs/heads/%s" % options.dest_series
139
                    # Sometimes it takes LP a while to notice the new ref
140
                    for i in range(10):
141
                        if lp_git_repo.getRefByPath(path=new_ref):
142
                            lp_git_repo.default_branch = new_ref
143
                            lp_git_repo.lp_save()
144
                            break
145
                        time.sleep(1)
146
                    else:
147
                        raise Exception(
148
                            "Was unable to set default_branch of %s after "
149
                            "multiple retries - proceed manually." % remote)
1143.1.3 by Simon Quigley
Make adjustments from Colin's suggestions.
150
                else:
1150 by Colin Watson
branch-seeds: whitespace nits
151
                    raise Exception(
152
                        "Git remote URL must be on git.launchpad.net.")
1143.1.1 by Simon Quigley
Automatically change the default branch in Launchpad once the new branch is made.
153
407 by Colin Watson
add new branch-seeds script to branch a set of seed collections when opening a new series
154
155
def main():
156
    parser = OptionParser(usage="usage: %prog [options] collection ...")
157
    parser.add_option(
158
        "-l", "--launchpad", dest="launchpad_instance", default="production")
559 by Colin Watson
Remove some hardcoded series names, picking them up by status from Launchpad instead.
159
    parser.add_option(
160
        "--source-series",
161
        help="source series (default: current stable release)")
162
    parser.add_option(
163
        "--dest-series",
164
        help="destination series (default: series in pre-release freeze)")
407 by Colin Watson
add new branch-seeds script to branch a set of seed collections when opening a new series
165
    options, args = parser.parse_args()
166
    if not args:
167
        parser.error("You must specify at least one seed collection.")
168
169
    options.launchpad = Launchpad.login_with(
1143.1.1 by Simon Quigley
Automatically change the default branch in Launchpad once the new branch is made.
170
        "branch-seeds", options.launchpad_instance, version="devel")
407 by Colin Watson
add new branch-seeds script to branch a set of seed collections when opening a new series
171
559 by Colin Watson
Remove some hardcoded series names, picking them up by status from Launchpad instead.
172
    distro = options.launchpad.distributions["ubuntu"]
173
    if options.source_series is None:
174
        options.source_series = [
175
            series.name for series in distro.series
176
            if series.status == "Current Stable Release"][0]
177
    if options.dest_series is None:
178
        options.dest_series = [
179
            series.name for series in distro.series
180
            if series.status == "Pre-release Freeze"][0]
181
407 by Colin Watson
add new branch-seeds script to branch a set of seed collections when opening a new series
182
    for collection in args:
183
        branch(options, collection)
184
185
186
main()