source: trunk/packages/invirt-dev/python/invirt/builder.py @ 3048

Last change on this file since 3048 was 3042, checked in by gdb, 14 years ago

Don't require that a pocket exists when validating

File size: 5.5 KB
Line 
1"""Invirt build utilities.
2
3This module contains utility functions used by both the invirtibuilder
4and the remctl submission scripts that insert items into its queue.
5"""
6
7
8import os
9import subprocess
10
11from debian_bundle import changelog
12
13import invirt.common as c
14from invirt.config import structs as config
15
16
17_QUEUE_DIR = '/var/lib/invirt-dev/queue'
18_REPO_DIR = '/srv/git'
19_LOG_DIR = '/var/log/invirt/builds'
20_HOOKS_DIR = '/usr/share/invirt-dev/build-hooks'
21
22
23class InvalidBuild(ValueError):
24    pass
25
26
27def getRepo(package):
28    """Return the path to the git repo for a given package."""
29    return os.path.join(_REPO_DIR, 'invirt/packages', '%s.git' % package)
30
31def ensureValidPackage(package):
32    """Perform some basic sanity checks that the requested repo is in a
33    subdirectory of _REPO_DIR/invirt/packages.  This prevents weirdness
34    such as submitting a package like '../prod/...git'.  Also ensures that
35    the repo exists."""
36    # TODO: this might be easier just to regex
37    repo = os.path.abspath(getRepo(package))
38    parent_dir = os.path.dirname(repo)
39    prefix = os.path.join(_REPO_DIR, 'invirt/packages')
40    if not parent_dir.startswith(prefix):
41        raise InvalidBuild('Invalid package name %s' % package)
42    elif not os.path.exists(repo):
43        raise InvalidBuild('Nonexisting package %s' % package)
44
45def canonicalize_commit(package, commit, shorten=False):
46    if shorten:
47        flags = ['--short']
48    else:
49        flags = []
50    return c.captureOutput(['git', 'rev-parse'] + flags + [commit],
51                           cwd=getRepo(package)).strip()
52
53def pocketToGit(pocket):
54    """Map a pocket in the configuration to a git branch."""
55    return getattr(getattr(config.build.pockets, pocket), 'git', pocket)
56
57
58def pocketToApt(pocket):
59    """Map a pocket in the configuration to an apt repo pocket."""
60    return getattr(getattr(config.build.pockets, pocket), 'apt', pocket)
61
62
63def getGitFile(package, ref, path):
64    """Return the contents of a path from a git ref in a package."""
65    return c.captureOutput(['git', 'cat-file', 'blob', '%s:%s' % (ref, path)],
66                           cwd=getRepo(package))
67
68
69def getChangelog(package, ref):
70    """Get a changelog object for a given ref in a given package.
71
72    This returns a debian_bundle.changelog.Changelog object for a
73    given ref of a given package.
74    """
75    return changelog.Changelog(getGitFile(package, ref, 'debian/changelog'))
76
77def runHook(hook, args=[], stdin_str=None):
78    """Run a named hook."""
79    hook = os.path.join(_HOOKS_DIR, hook)
80    try:
81        c.captureOutput([hook] + args, stdin_str=stdin_str)
82    except OSError:
83        pass
84
85def getVersion(package, ref):
86    """Get the version of a given package at a particular ref."""
87    return getChangelog(package, ref).get_version()
88
89
90def validateBuild(pocket, package, commit):
91    """Given the parameters of a new build, validate that build.
92
93    The checks this function performs vary based on whether or not the
94    pocket is configured with allow_backtracking.
95
96    A build of a pocket without allow_backtracking set must be a
97    fast-forward of the previous revision, and the most recent version
98    in the changelog most be strictly greater than the version
99    currently in the repository.
100
101    In all cases, this revision of the package can only have the same
102    version number as any other revision currently in the apt
103    repository if they have the same commit ID.
104
105    If it's unspecified, it is assumed that pocket do not
106    allow_backtracking.
107
108    If this build request fails validation, this function will raise a
109    InvalidBuild exception, with information about why the validation
110    failed.
111
112    If this build request can be satisfied by copying the package from
113    another pocket, then this function returns that pocket. Otherwise,
114    it returns True.
115    """
116    ensureValidPackage(package)
117    package_repo = getRepo(package)
118    new_version = getVersion(package, commit)
119
120    ret = True
121
122    for p in config.build.pockets:
123        if p == pocket:
124            continue
125
126        b = pocketToGit(p)
127        try:
128            current_commit = c.captureOutput(['git', 'rev-parse', b],
129                                             cwd=package_repo).strip()
130        except subprocess.CalledProcessError:
131            # Guess we haven't created this pocket yet
132            continue
133
134        current_version = getVersion(package, b)
135
136        if current_version == new_version:
137            if current_commit == commit:
138                ret = p
139            else:
140                raise InvalidBuild('Version %s of %s already available is in '
141                                   'pocket %s from commit %s' %
142                                   (new_version, package, p, current_commit))
143
144    if not config.build.pockets[pocket].get('allow_backtracking', False):
145        branch = pocketToGit(pocket)
146        current_version = getVersion(package, branch)
147        if new_version <= current_version:
148            raise InvalidBuild('New version %s of %s is not newer than '
149                               'version %s currently in pocket %s' %
150                               (new_version, package, current_version, pocket))
151
152        # Almost by definition, A is a fast-forward of B if B..A is
153        # empty
154        if not c.captureOutput(['git', 'rev-list', '%s..%s' % (commit, branch)],
155                               cwd=package_repo):
156            raise InvalidBuild('New commit %s of %s is not a fast-forward of'
157                               'commit currently in pocket %s' %
158                               (commit, package, pocket))
159
160    return ret
Note: See TracBrowser for help on using the repository browser.