1
# Copyright 2014 Canonical Ltd. All rights reserved.
13
from bzrlib.branch import Branch
14
from bzrlib.plugin import load_plugins
16
from bzrlib.plugins.launchpad import account as lp_account
18
if 'GlobalConfig' in dir(lp_account):
19
from bzrlib.config import LocationConfig as LocationConfiguration
20
_ = LocationConfiguration
22
from bzrlib.config import LocationStack as LocationConfiguration
23
_ = LocationConfiguration
26
def get_ubunet_branch_config(config_file):
28
Retrieves the sourcedeps configuration for an ubunet source dir.
29
Returns a dict of (branch, revspec) tuples, keyed by branch name.
32
with open(config_file, 'r') as stream:
34
line = line.split('#')[0].strip()
35
match = re.match(r'(\S+)\s+'
36
'bzr\+ssh://([^/]+)/([^;]+)'
37
'(?:;revno=(\d+))?', line)
39
name, host, branch, revno = match.group(1, 2, 3, 4)
44
branches[name] = (host, branch, revspec)
48
def main(config_file, parent_dir, target_dir, verbose):
52
os.makedirs(parent_dir)
54
if e.errno != errno.EEXIST:
57
bzr_config = LocationConfiguration(parent_dir)
58
get_lp_login = lp_account.get_lp_login
59
username = get_lp_login(bzr_config) or get_lp_login()
61
raise RuntimeError("Unable to determine launchpad login")
62
quoted_username = urllib.quote(username)
64
branches = sorted(get_ubunet_branch_config(config_file).iteritems())
65
for branch_name, (host, quoted_branch_spec, revspec) in branches:
68
# qualify mirror branch name with hash of remote repo path to deal
69
# with changes to the remote branch URL over time
70
branch_spec_digest = hashlib.sha1(quoted_branch_spec).hexdigest()
71
branch_directory = branch_spec_digest
73
source_path = os.path.join(parent_dir, branch_directory)
74
destination_path = os.path.join(target_dir, branch_name)
76
# Remove leftover symlinks/stray files.
78
os.remove(destination_path)
80
if e.errno != errno.EISDIR and e.errno != errno.ENOENT:
83
branch_url = ("bzr+ssh://%s@%s/%s" %
84
(quoted_username, host, quoted_branch_spec))
85
lp_url = "lp:" + quoted_branch_spec.replace("+branch/", "")
87
# Create the local mirror branch if it doesn't already exist
89
sys.stderr.write('%30s: ' % (branch_name,))
93
if not os.path.exists(source_path):
94
subprocess.check_call(['bzr', 'branch', '-q',
95
'--', branch_url, source_path])
98
source_branch = Branch.open(source_path)
100
# Freshen the source branch if required (-1 means we want tip).
101
if not fresh and (revno == -1 or revno > source_branch.revno()):
102
subprocess.check_call(['bzr', 'pull', '-q', '--overwrite', '-r',
103
str(revno), '-d', source_path,
106
if os.path.exists(destination_path):
107
# Overwrite the destination with the appropriate revision.
108
subprocess.check_call(['bzr', 'clean-tree', '--force', '-q',
109
'--ignored', '-d', destination_path])
110
subprocess.check_call(['bzr', 'pull', '-q', '--overwrite',
112
'-d', destination_path, '--', source_path])
114
# Create a new branch.
115
subprocess.check_call(['bzr', 'branch', '-q', '--hardlink',
117
'--', source_path, destination_path])
119
# Check the state of the destination branch.
120
destination_branch = Branch.open(destination_path)
121
destination_revno = destination_branch.revno()
124
sys.stderr.write('checked out %4s of %s\n' %
125
("r" + str(destination_revno), lp_url))
128
if revno != -1 and destination_revno != revno:
129
raise RuntimeError("Expected revno %d but got revno %d" %
130
(revno, destination_revno))
132
if __name__ == '__main__':
133
parser = optparse.OptionParser(
134
usage="%prog [options]",
136
"Add a lightweight checkout in <target> for each "
137
"corresponding file in <parent>."),
138
add_help_option=False)
140
'-p', '--parent', dest='parent', default=None,
141
help=("The directory of the parent tree."),
144
'-t', '--target', dest='target', default=curdir,
145
help=("The directory of the target tree."),
148
'-c', '--config', dest='config', default=None,
149
help=("The config file to be used for config-manager."),
152
'-q', '--quiet', dest='verbose', action='store_false',
153
help="Be less verbose.")
155
'-h', '--help', action='help',
156
help="Show this help message and exit.")
157
parser.set_defaults(verbose=True)
159
options, args = parser.parse_args()
161
if options.parent is None:
163
"Parent directory not specified.")
165
if options.target is None:
167
"Target directory not specified.")
169
sys.exit(main(config_file=options.config,
170
parent_dir=options.parent,
171
target_dir=options.target,
172
verbose=options.verbose))