This file is indexed.

/usr/bin/grok-manifest is in grokmirror 0.3.5-1.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
#! /usr/bin/python -tt
# Copyright (C) 2013 by The Linux Foundation and contributors
#
# 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, either version 3 of the License, or
# (at your option) any later version.
# 
# 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, see <http://www.gnu.org/licenses/>.

import os
import sys
import logging
import time

import grokmirror

from git import Repo

logger = logging.getLogger(__name__)

def update_manifest(manifest, toplevel, gitdir, usenow):
    path = gitdir.replace(toplevel, '', 1)

    # Try to open git dir
    logger.debug('Examining %s' % gitdir)
    try:
        repo = Repo(gitdir)
        assert repo.bare == True
    except:
        logger.critical('Error opening %s.' % gitdir)
        logger.critical('Make sure it is a bare git repository.')
        sys.exit(1)

    # Ignore it if it's an empty git repository
    try:
        if len(repo.heads) == 0:
            logger.info('%s has no heads, ignoring' % gitdir)
            return
    except:
        # Errors when listing heads usually means repository is no good
        logger.info('Error listing heads in %s, ignoring' % gitdir)
        return

    try:
        description = repo.description
    except:
        description = 'Unnamed repository'

    try:
        rcr   = repo.config_reader()
        owner = rcr.get('gitweb', 'owner')
    except:
        owner = None

    modified = 0

    if not usenow:
        for branch in repo.branches:
            try:
                if branch.commit.committed_date > modified:
                    modified = branch.commit.committed_date
                    # Older versions of GitPython returned time.struct_time
                    if type(modified) == time.struct_time:
                        modified = int(time.mktime(modified))

            except:
                pass

    if modified == 0:
        modified = int(time.time())

    reference = None

    if len(repo.alternates) == 1:
        # use this to hint which repo to use as reference when cloning
        alternate = repo.alternates[0]
        if alternate.find(toplevel) == 0:
            reference = alternate.replace(toplevel, '').replace('/objects', '')

    if path not in manifest.keys():
        logger.info('Adding %s to manifest' % path)
        manifest[path] = {}
    else:
        logger.info('Updating %s in the manifest' % path)

    manifest[path]['owner']       = owner
    manifest[path]['description'] = description
    manifest[path]['reference']   = reference
    manifest[path]['modified']    = modified

def set_symlinks(manifest, toplevel, symlinks):
    for symlink in symlinks:
        target = os.path.realpath(symlink)
        if target.find(toplevel) < 0:
            logger.info('Symlink %s points outside toplevel, ignored' % symlink)
            continue
        tgtgitdir = target.replace(toplevel, '')
        if tgtgitdir not in manifest.keys():
            logger.info('Symlink %s points to %s, which we do not recognize'
                    % (symlink, target))
            continue
        relative = symlink.replace(toplevel, '')
        if 'symlinks' in manifest[tgtgitdir].keys():
            if relative not in manifest[tgtgitdir]['symlinks']:
                logger.info('Recording symlink %s->%s' % (relative, tgtgitdir))
                manifest[tgtgitdir]['symlinks'].append(relative)
        else:
            manifest[tgtgitdir]['symlinks'] = [relative]
            logger.info('Recording symlink %s to %s' % (relative, tgtgitdir))

        # Now go through all repos and fix any references pointing to the
        # symlinked location.
        for gitdir in manifest.keys():
            if manifest[gitdir]['reference'] == relative:
                logger.info('Adjusted symlinked reference for %s: %s->%s'
                        % (gitdir, relative, tgtgitdir))
                manifest[gitdir]['reference'] = tgtgitdir

def purge_manifest(manifest, toplevel, gitdirs):
    for oldrepo in manifest.keys():
        if os.path.join(toplevel, oldrepo.lstrip('/')) not in gitdirs:
            logger.info('Purged deleted %s\n' % oldrepo)
            del manifest[oldrepo]

if __name__ == '__main__':
    from optparse import OptionParser

    usage = '''usage: %prog -m manifest.js[.gz] -t /path [/path/to/bare.git]
    Create or update manifest.js with the latest repository information.
    '''

    parser = OptionParser(usage=usage, version=grokmirror.VERSION)
    parser.add_option('-m', '--manifest', dest='manifile',
        help='Location of manifest.js or manifest.js.gz')
    parser.add_option('-t', '--toplevel', dest='toplevel',
        help='Top dir where all repositories reside')
    parser.add_option('-l', '--logfile', dest='logfile',
        default=None,
        help='When specified, will put debug logs in this location')
    parser.add_option('-n', '--use-now', dest='usenow', action='store_true',
        default=False,
        help='Use current timestamp instead of parsing commits')
    parser.add_option('-c', '--check-export-ok', dest='check_export_ok',
        action='store_true', default=False,
        help='Export only repositories marked as git-daemon-export-ok')
    parser.add_option('-p', '--purge', dest='purge', action='store_true',
        default=False,
        help='Purge deleted git repositories from manifest')
    parser.add_option('-x', '--remove', dest='remove', action='store_true',
        default=False,
        help='Remove repositories passed as arguments from manifest')
    parser.add_option('-y', '--pretty', dest='pretty', action='store_true',
        default=False,
        help='Pretty-print manifest (sort keys and add indentation)')
    parser.add_option('-i', '--ignore-paths', dest='ignore', action='append',
        default=[],
        help='When finding git dirs, ignore these paths '
             '(can be used multiple times, accepts shell-style globbing)')
    parser.add_option('-w', '--wait-for-manifest', dest='wait',
        action='store_true', default=False,
        help='When running with arguments, wait if manifest is not there '
             '(can be useful when multiple writers are writing the manifest)')
    parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
        default=False,
        help='Be verbose and tell us what you are doing')

    (opts, args) = parser.parse_args()

    if not opts.manifile:
        parser.error('You must provide the path to the manifest file')
    if not opts.toplevel:
        parser.error('You must provide the toplevel path')
    if not len(args) and opts.wait:
        parser.error('--wait option only makes sense when dirs are passed')

    logger.setLevel(logging.DEBUG)

    ch = logging.StreamHandler()
    formatter = logging.Formatter('%(message)s')
    ch.setFormatter(formatter)

    if opts.verbose:
        ch.setLevel(logging.INFO)
    else:
        ch.setLevel(logging.CRITICAL)

    logger.addHandler(ch)

    if opts.logfile is not None:
        ch = logging.FileHandler(opts.logfile)
        formatter = logging.Formatter("[%(process)d] %(asctime)s - %(levelname)s - %(message)s")
        ch.setFormatter(formatter)

        ch.setLevel(logging.DEBUG)
        logger.addHandler(ch)

    # push our logger into grokmirror to override the default
    grokmirror.logger = logger

    grokmirror.manifest_lock(opts.manifile)
    manifest = grokmirror.read_manifest(opts.manifile, wait=opts.wait)

    # If manifest is empty, don't use current timestamp
    if not len(manifest.keys()):
        opts.usenow = False

    if opts.remove and len(args):
        # Remove the repos as required, write new manfiest and exit
        for fullpath in args:
            repo = fullpath.replace(opts.toplevel, '', 1)
            if repo in manifest.keys():
                del manifest[repo]
                logger.info('Repository %s removed from manifest' % repo)
            else:
                logger.info('Repository %s not in manifest' % repo)

        # XXX: need to add logic to make sure we don't break the world
        #      by removing a repository used as a reference for others
        #      also make sure we clean up any dangling symlinks

        grokmirror.write_manifest(opts.manifile, manifest, pretty=opts.pretty)
        grokmirror.manifest_unlock(opts.manifile)
        sys.exit(0)

    if opts.purge or not len(args) or not len(manifest.keys()):
        # We automatically purge when we do a full tree walk
        gitdirs = grokmirror.find_all_gitdirs(opts.toplevel, ignore=opts.ignore)
        purge_manifest(manifest, opts.toplevel, gitdirs)

    if len(manifest.keys()) and len(args):
        # limit ourselves to passed dirs only when there is something
        # in the manifest. This precaution makes sure we regenerate the
        # whole file when there is nothing in it or it can't be parsed.
        gitdirs = args

    symlinks = []
    for gitdir in gitdirs:
        # check to make sure this gitdir is ok to export
        if (opts.check_export_ok and
            not os.path.exists(os.path.join(gitdir, 'git-daemon-export-ok'))):
            # is it curently in the manifest?
            repo = gitdir.replace(opts.toplevel, '', 1)
            if repo in manifest.keys():
                logger.info('Repository %s is no longer exported, '
                    'removing from manifest' % repo)
                del manifest[repo]

            # XXX: need to add logic to make sure we don't break the world
            #      by removing a repository used as a reference for others
            #      also make sure we clean up any dangling symlinks
            continue

        if os.path.islink(gitdir):
            symlinks.append(gitdir)
        else:
            update_manifest(manifest, opts.toplevel, gitdir, opts.usenow)

    if len(symlinks):
        set_symlinks(manifest, opts.toplevel, symlinks)

    grokmirror.write_manifest(opts.manifile, manifest, pretty=opts.pretty)
    grokmirror.manifest_unlock(opts.manifile)