summaryrefslogtreecommitdiff
path: root/morphlib/plugins/list_artifacts_plugin.py
blob: 5e64f708f10b43521dceff7046172fc15ba39888 (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
# Copyright (C) 2014  Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

# This plugin is used as part of the Baserock automated release process.
#
# See: <http://wiki.baserock.org/guides/release-process> for more information.


import cliapp
import morphlib


class ListArtifactsPlugin(cliapp.Plugin):

    def enable(self):
        self.app.add_subcommand(
            'list-artifacts', self.list_artifacts,
            arg_synopsis='REPO REF MORPH [MORPH]...')

    def disable(self):
        pass

    def list_artifacts(self, args):
        '''List every artifact in the build graph of a system.

        Command line arguments:

        * `REPO` is a git repository URL.
        * `REF` is a branch or other commit reference in that repository.
        * `MORPH` is a system morphology name at that ref.

        You can pass multiple values for `MORPH`, in which case the command
        outputs the union of the build graphs of all the systems passed in.

        The output includes any meta-artifacts such as .meta and .build-log
        files.

        '''

        if len(args) < 3:
            raise cliapp.AppException(
                'Wrong number of arguments to list-artifacts command '
                '(see help)')

        repo, ref = args[0], args[1]
        system_names = map(morphlib.util.strip_morph_extension, args[2:])

        self.lrc, self.rrc = morphlib.util.new_repo_caches(self.app)
        self.resolver = morphlib.artifactresolver.ArtifactResolver()

        artifact_files = set()
        for system_name in system_names:
            system_artifact_files = self.list_artifacts_for_system(
                repo, ref, system_name)
            artifact_files.update(system_artifact_files)

        for artifact_file in sorted(artifact_files):
            print artifact_file

    def list_artifacts_for_system(self, repo, ref, system_name):
        '''List all artifact files in the build graph of a single system.'''

        # Sadly, we must use a fresh source pool and a fresh list of artifacts
        # for each system. Creating a source pool is slow (queries every Git
        # repo involved in the build) and resolving artifacts isn't so quick
        # either. Unfortunately, each Source object can only have one set of
        # Artifact objects associated, which means the source pool cannot mix
        # sources that are being built for multiple architectures: the build
        # graph representation does not distinguish chunks or strata of
        # different architectures right now.

        self.app.status(
            msg='Creating source pool for %s' % system_name, chatty=True)
        source_pool = self.app.create_source_pool(
            self.lrc, self.rrc, (repo, ref, system_name + '.morph'))

        self.app.status(
            msg='Resolving artifacts for %s' % system_name, chatty=True)
        artifacts = self.resolver.resolve_artifacts(source_pool)

        def find_artifact_by_name(artifacts_list, name):
            for a in artifacts_list:
                if a.source.filename == name + '.morph':
                    return a
            raise ValueError

        system_artifact = find_artifact_by_name(artifacts, system_name)

        self.app.status(
            msg='Computing cache keys for %s' % system_name, chatty=True)
        build_env = morphlib.buildenvironment.BuildEnvironment(
            self.app.settings, system_artifact.source.morphology['arch'])
        ckc = morphlib.cachekeycomputer.CacheKeyComputer(build_env)

        artifact_files = set()
        for artifact in system_artifact.walk():
            artifact.cache_key = ckc.compute_key(artifact)
            artifact.cache_id = ckc.get_cache_id(artifact)

            artifact_files.add(artifact.basename())

            if artifact.source.morphology.needs_artifact_metadata_cached:
                artifact_files.add('%s.meta' % artifact.basename())

            # This is unfortunate hardwiring of behaviour; in future we
            # should list all artifacts in the meta-artifact file, so we
            # don't have to guess what files there will be.
            artifact_files.add('%s.meta' % artifact.cache_key)
            if artifact.source.morphology['kind'] == 'chunk':
                artifact_files.add('%s.build-log' % artifact.cache_key)

        return artifact_files