GITC: Always update the gitc manifest from the repo manifest

This way any changes made to the main manifest are reflected in the gitc
manifest. It's also necessary to use both manifests to sync since the
information required to update the gitc manifest is actually in the repo
manifest.

This also fixes a few issues that came up when testing. notdefault
groups weren't being saved to the gitc manifest in a method that matched
'sync'. The merge branch wasn't always being set to the correct value
either.

Change-Id: I435235cb5622a048ffad0059affd32ecf71f1f5b
This commit is contained in:
Dan Willemsen 2015-09-08 13:27:20 -07:00
parent 5cc384034d
commit 5ea32d1359
5 changed files with 104 additions and 36 deletions

View File

@ -15,6 +15,8 @@
from __future__ import print_function from __future__ import print_function
import os import os
import platform
import re
import sys import sys
import time import time
@ -22,7 +24,6 @@ import git_command
import git_config import git_config
import wrapper import wrapper
GITC_FS_ROOT_DIR = '/gitc/manifest-rw/' GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
NUM_BATCH_RETRIEVE_REVISIONID = 300 NUM_BATCH_RETRIEVE_REVISIONID = 300
@ -65,26 +66,82 @@ def _set_project_revisions(projects):
sys.exit(1) sys.exit(1)
proj.revisionExpr = gitcmd.stdout.split('\t')[0] proj.revisionExpr = gitcmd.stdout.split('\t')[0]
def generate_gitc_manifest(client_dir, manifest, projects=None): def _manifest_groups(manifest):
"""Returns the manifest group string that should be synced
This is the same logic used by Command.GetProjects(), which is used during
repo sync
@param manifest: The XmlManifest object
"""
mp = manifest.manifestProject
groups = mp.config.GetString('manifest.groups')
if not groups:
groups = 'default,platform-' + platform.system().lower()
return groups
def generate_gitc_manifest(gitc_manifest, manifest, paths=None):
"""Generate a manifest for shafsd to use for this GITC client. """Generate a manifest for shafsd to use for this GITC client.
@param client_dir: GITC client directory to install the .manifest file in. @param gitc_manifest: Current gitc manifest, or None if there isn't one yet.
@param manifest: XmlManifest object representing the repo manifest. @param manifest: A GitcManifest object loaded with the current repo manifest.
@param projects: List of projects we want to update, this must be a sublist @param paths: List of project paths we want to update.
of manifest.projects to work properly. If not provided,
manifest.projects is used.
""" """
print('Generating GITC Manifest by fetching revision SHAs for each ' print('Generating GITC Manifest by fetching revision SHAs for each '
'project.') 'project.')
if projects is None: if paths is None:
projects = manifest.projects paths = manifest.paths.keys()
groups = [x for x in re.split(r'[,\s]+', _manifest_groups(manifest)) if x]
# Convert the paths to projects, and filter them to the matched groups.
projects = [manifest.paths[p] for p in paths]
projects = [p for p in projects if p.MatchesGroups(groups)]
if gitc_manifest is not None:
for path, proj in manifest.paths.iteritems():
if not proj.MatchesGroups(groups):
continue
if not proj.upstream and not git_config.IsId(proj.revisionExpr):
proj.upstream = proj.revisionExpr
if not path in gitc_manifest.paths:
# Any new projects need their first revision, even if we weren't asked
# for them.
projects.append(proj)
elif not path in paths:
# And copy revisions from the previous manifest if we're not updating
# them now.
gitc_proj = gitc_manifest.paths[path]
if gitc_proj.old_revision:
proj.revisionExpr = None
proj.old_revision = gitc_proj.old_revision
else:
proj.revisionExpr = gitc_proj.revisionExpr
index = 0 index = 0
while index < len(projects): while index < len(projects):
_set_project_revisions( _set_project_revisions(
projects[index:(index+NUM_BATCH_RETRIEVE_REVISIONID)]) projects[index:(index+NUM_BATCH_RETRIEVE_REVISIONID)])
index += NUM_BATCH_RETRIEVE_REVISIONID index += NUM_BATCH_RETRIEVE_REVISIONID
if gitc_manifest is not None:
for path, proj in gitc_manifest.paths.iteritems():
if proj.old_revision and path in paths:
# If we updated a project that has been started, keep the old-revision
# updated.
repo_proj = manifest.paths[path]
repo_proj.old_revision = repo_proj.revisionExpr
repo_proj.revisionExpr = None
# Convert URLs from relative to absolute.
for name, remote in manifest.remotes.iteritems():
remote.fetchUrl = remote.resolvedFetchUrl
# Save the manifest. # Save the manifest.
save_manifest(manifest, client_dir=client_dir) save_manifest(manifest)
def save_manifest(manifest, client_dir=None): def save_manifest(manifest, client_dir=None):
"""Save the manifest file in the client_dir. """Save the manifest file in the client_dir.
@ -95,7 +152,7 @@ def save_manifest(manifest, client_dir=None):
if not client_dir: if not client_dir:
client_dir = manifest.gitc_client_dir client_dir = manifest.gitc_client_dir
with open(os.path.join(client_dir, '.manifest'), 'w') as f: with open(os.path.join(client_dir, '.manifest'), 'w') as f:
manifest.Save(f) manifest.Save(f, groups=_manifest_groups(manifest))
# TODO(sbasi/jorg): Come up with a solution to remove the sleep below. # TODO(sbasi/jorg): Come up with a solution to remove the sleep below.
# Give the GITC filesystem time to register the manifest changes. # Give the GITC filesystem time to register the manifest changes.
time.sleep(3) time.sleep(3)

View File

@ -167,11 +167,12 @@ class XmlManifest(object):
def _ParseGroups(self, groups): def _ParseGroups(self, groups):
return [x for x in re.split(r'[,\s]+', groups) if x] return [x for x in re.split(r'[,\s]+', groups) if x]
def Save(self, fd, peg_rev=False, peg_rev_upstream=True): def Save(self, fd, peg_rev=False, peg_rev_upstream=True, groups=None):
"""Write the current manifest out to the given file descriptor. """Write the current manifest out to the given file descriptor.
""" """
mp = self.manifestProject mp = self.manifestProject
if groups is None:
groups = mp.config.GetString('manifest.groups') groups = mp.config.GetString('manifest.groups')
if groups: if groups:
groups = self._ParseGroups(groups) groups = self._ParseGroups(groups)

View File

@ -19,6 +19,7 @@ import sys
import gitc_utils import gitc_utils
from command import RequiresGitcCommand from command import RequiresGitcCommand
from manifest_xml import GitcManifest
from subcmds import init from subcmds import init
@ -68,15 +69,16 @@ use for this GITC client.
os.mkdir(self.client_dir) os.mkdir(self.client_dir)
super(GitcInit, self).Execute(opt, args) super(GitcInit, self).Execute(opt, args)
for name, remote in self.manifest.remotes.iteritems(): manifest_file = self.manifest.manifestFile
remote.fetchUrl = remote.resolvedFetchUrl
if opt.manifest_file: if opt.manifest_file:
if not os.path.exists(opt.manifest_file): if not os.path.exists(opt.manifest_file):
print('fatal: Specified manifest file %s does not exist.' % print('fatal: Specified manifest file %s does not exist.' %
opt.manifest_file) opt.manifest_file)
sys.exit(1) sys.exit(1)
self.manifest.Override(opt.manifest_file) manifest_file = opt.manifest_file
gitc_utils.generate_gitc_manifest(self.client_dir, self.manifest)
manifest = GitcManifest(self.repodir, opt.gitc_client)
manifest.Override(manifest_file)
gitc_utils.generate_gitc_manifest(None, manifest)
print('Please run `cd %s` to view your GITC client.' % print('Please run `cd %s` to view your GITC client.' %
os.path.join(gitc_utils.GITC_FS_ROOT_DIR, opt.gitc_client)) os.path.join(gitc_utils.GITC_FS_ROOT_DIR, opt.gitc_client))

View File

@ -57,7 +57,6 @@ revision specified in the manifest.
print("error: at least one project must be specified", file=sys.stderr) print("error: at least one project must be specified", file=sys.stderr)
sys.exit(1) sys.exit(1)
proj_name_to_gitc_proj_dict = {}
if self.gitc_manifest: if self.gitc_manifest:
all_projects = self.GetProjects(projects, manifest=self.gitc_manifest, all_projects = self.GetProjects(projects, manifest=self.gitc_manifest,
missing_ok=True) missing_ok=True)
@ -67,7 +66,6 @@ revision specified in the manifest.
else: else:
project.already_synced = False project.already_synced = False
project.old_revision = project.revisionExpr project.old_revision = project.revisionExpr
proj_name_to_gitc_proj_dict[project.name] = project
project.revisionExpr = None project.revisionExpr = None
# Save the GITC manifest. # Save the GITC manifest.
gitc_utils.save_manifest(self.gitc_manifest) gitc_utils.save_manifest(self.gitc_manifest)
@ -77,9 +75,10 @@ revision specified in the manifest.
pm = Progress('Starting %s' % nb, len(all_projects)) pm = Progress('Starting %s' % nb, len(all_projects))
for project in all_projects: for project in all_projects:
pm.update() pm.update()
if self.gitc_manifest: if self.gitc_manifest:
gitc_project = proj_name_to_gitc_proj_dict[project.name] gitc_project = self.gitc_manifest.paths[project.relpath]
# Sync projects that have already been opened. # Sync projects that have not been opened.
if not gitc_project.already_synced: if not gitc_project.already_synced:
proj_localdir = os.path.join(self.gitc_manifest.gitc_client_dir, proj_localdir = os.path.join(self.gitc_manifest.gitc_client_dir,
project.relpath) project.relpath)
@ -89,7 +88,7 @@ revision specified in the manifest.
project.Sync_NetworkHalf() project.Sync_NetworkHalf()
sync_buf = SyncBuffer(self.manifest.manifestProject.config) sync_buf = SyncBuffer(self.manifest.manifestProject.config)
project.Sync_LocalHalf(sync_buf) project.Sync_LocalHalf(sync_buf)
project.revisionExpr = gitc_project.old_revision project.revisionId = gitc_project.old_revision
# If the current revision is a specific SHA1 then we can't push back # If the current revision is a specific SHA1 then we can't push back
# to it; so substitute with dest_branch if defined, or with manifest # to it; so substitute with dest_branch if defined, or with manifest
@ -100,6 +99,7 @@ revision specified in the manifest.
branch_merge = project.dest_branch branch_merge = project.dest_branch
else: else:
branch_merge = self.manifest.default.revisionExpr branch_merge = self.manifest.default.revisionExpr
if not project.StartBranch(nb, branch_merge=branch_merge): if not project.StartBranch(nb, branch_merge=branch_merge):
err.append(project) err.append(project)
pm.end() pm.end()

View File

@ -75,6 +75,7 @@ from error import RepoChangedException, GitError, ManifestParseError
from project import SyncBuffer from project import SyncBuffer
from progress import Progress from progress import Progress
from wrapper import Wrapper from wrapper import Wrapper
from manifest_xml import GitcManifest
_ONE_DAY_S = 24 * 60 * 60 _ONE_DAY_S = 24 * 60 * 60
@ -670,32 +671,39 @@ later is required to fix a server side protocol bug.
if opt.jobs is None: if opt.jobs is None:
self.jobs = self.manifest.default.sync_j self.jobs = self.manifest.default.sync_j
# TODO (sbasi) - Add support for manifest changes, aka projects
# have been added or deleted from the manifest.
if self.gitc_manifest: if self.gitc_manifest:
gitc_manifest_projects = self.GetProjects(args, gitc_manifest_projects = self.GetProjects(args,
manifest=self.gitc_manifest,
missing_ok=True) missing_ok=True)
gitc_projects = [] gitc_projects = []
opened_projects = [] opened_projects = []
for project in gitc_manifest_projects: for project in gitc_manifest_projects:
if not project.old_revision: if project.relpath in self.gitc_manifest.paths and \
gitc_projects.append(project) self.gitc_manifest.paths[project.relpath].old_revision:
opened_projects.append(project.relpath)
else: else:
opened_projects.append(project) gitc_projects.append(project.relpath)
if gitc_projects and not opt.local_only: if not args:
gitc_projects = None
if gitc_projects != [] and not opt.local_only:
print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name) print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
gitc_utils.generate_gitc_manifest(self.gitc_manifest.gitc_client_dir, manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
self.gitc_manifest, if manifest_name:
manifest.Override(manifest_name)
else:
manifest.Override(self.manifest.manifestFile)
gitc_utils.generate_gitc_manifest(self.gitc_manifest,
manifest,
gitc_projects) gitc_projects)
print('GITC client successfully synced.') print('GITC client successfully synced.')
# The opened projects need to be synced as normal, therefore we # The opened projects need to be synced as normal, therefore we
# generate a new args list to represent the opened projects. # generate a new args list to represent the opened projects.
args = [] # TODO: make this more reliable -- if there's a project name/path overlap,
for proj in opened_projects: # this may choose the wrong project.
args.append(os.path.relpath(proj.worktree, os.getcwd())) args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
for p in opened_projects]
if not args: if not args:
return return
all_projects = self.GetProjects(args, all_projects = self.GetProjects(args,