7 from wizard import command, deploy, shell, util
10 options, args = parse_args(argv, baton)
14 shell.drop_priviledges(options)
16 logging.info("Migrating %s" % dir)
17 logging.debug("uid is %d" % os.getuid())
19 check_if_already_migrated(options)
21 deployment = make_deployment() # uses chdir
22 version = deployment.app_version
23 repo = version.application.repository(options.srv_path)
24 tag = version.scripts_tag
26 os.unsetenv("GIT_DIR") # prevent some perverse errors
28 sh = shell.Shell(options.dry_run)
29 check_if_tag_exists(sh, repo, tag)
30 make_repository(sh, options, repo, tag)
31 check_variables(deployment, options)
33 if not options.dry_run:
34 deployment.scriptsifyVersion()
36 logging.info("# create .scripts/version containing \"%s-%s-scripts\"" % (deployment.application.name, deployment.version))
38 def parse_args(argv, baton):
39 usage = """usage: %prog migrate [ARGS] DIR
41 Migrates a directory to our Git-based autoinstall format.
42 Performs basic sanity checking and intelligently determines
43 what repository and tag to use.
45 This command is meant to be run as the owner of the install
46 it is upgrading (see the scripts AFS kernel patch). Do
47 NOT run this command as root."""
48 parser = command.WizardOptionParser(usage)
49 baton.push(parser, "srv_path")
50 parser.add_option("--dry-run", dest="dry_run", action="store_true",
51 default=False, help="Prints would would be run without changing anything")
52 parser.add_option("--force", "-f", dest="force", action="store_true",
53 default=False, help="If .git or .scripts directory already exists, delete them and migrate")
54 options, args = parser.parse_all(argv)
56 parser.error("too many arguments")
58 parser.error("must specify directory")
59 return (options, args)
61 def check_if_already_migrated(options):
62 # XXX: duplicates some logic with Deployment.migrated
63 if os.path.isdir(".git") or os.path.isdir(".scripts"):
65 raise AlreadyMigratedError(dir)
67 if os.path.isdir(".git"):
68 logging.warning("Force removing .git directory")
69 if not options.dry_run:
70 shutil.rmtree(".git.bak", ignore_errors=True)
71 os.rename(".git", ".git.bak")
72 if os.path.isdir(".scripts"):
73 logging.warning("Force removing .scripts directory")
74 if not options.dry_run: shutil.rmtree(".scripts")
76 def make_deployment():
78 return deploy.Deployment(".")
80 if e.errno == errno.ENOENT:
81 raise NotAutoinstallError(dir)
84 def check_if_tag_exists(sh, repo, tag):
85 # check if the version we're trying to convert exists. We assume
86 # a convention here, namely, v1.2.3-scripts is what we want. If
87 # you broke the convention... shame on you.
89 sh.call("git", "--git-dir", repo, "rev-parse", tag)
90 except shell.CallError:
91 raise NoTagError(version)
93 def make_repository(sh, options, repo, tag):
94 sh.call("git", "init") # create repository
95 # configure our alternates (to save space and make this quick)
96 data = os.path.join(repo, "objects")
97 file = ".git/objects/info/alternates"
98 if not options.dry_run:
99 alternates = open(file, "w")
100 alternates.write(data)
102 htaccess = open(".git/.htaccess", "w")
103 htaccess.write("Deny from all\n")
106 logging.info("# create %s containing \"%s\"" % (file, data))
107 logging.info('# create .htaccess containing "Deny from all"')
108 # configure our remote (this is merely for convenience; wizard scripts
109 # will not rely on this)
110 sh.call("git", "remote", "add", "origin", repo)
111 # configure what would normally be set up on a 'git clone' for consistency
112 sh.call("git", "config", "branch.master.remote", "origin")
113 sh.call("git", "config", "branch.master.merge", "refs/heads/master")
114 # perform the initial fetch
115 sh.call("git", "fetch", "origin")
116 # soft reset to our tag
117 sh.call("git", "reset", tag, "--")
118 # checkout the .scripts directory
119 sh.call("git", "checkout", ".scripts")
120 logging.info("Diffstat:\n" + sh.eval("git", "diff", "--stat"))
121 # commit user local changes
122 message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
125 message += "\nMigrated-by: " + util.get_operator_git()
126 except util.NoOperatorInfo:
128 sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
130 def check_variables(d, options):
131 """Attempt to extract variables and complain if some are missing."""
132 variables = d.extract()
133 for k,v in variables.items():
134 if v is None and k not in d.application.deprecated_keys:
135 logging.warning("Variable %s not found" % k)
137 logging.debug("Variable %s is %s" % (k,v))
139 class Error(command.Error):
140 """Base exception for all exceptions raised by migrate"""
143 class AlreadyMigratedError(Error):
144 def __init__(self, dir):
149 ERROR: Directory already contains a .git and/or
150 .scripts directory. Did you already migrate it?
153 class NotAutoinstallError(Error):
154 def __init__(self, dir):
159 ERROR: Could not find .scripts-version file. Are you sure
160 this is an autoinstalled application?
163 class NoTagError(Error):
164 def __init__(self, version):
165 self.version = version
169 ERROR: Could not find tag v%s-scripts in repository
170 for %s. Double check and make sure
171 the repository was prepared with all necessary tags!
172 """ % (self.version.version, self.version.application.name)