8 from wizard import command, deploy, shell, util
10 def main(argv, baton):
11 options, args = parse_args(argv, baton)
15 shell.drop_priviledges(options)
16 sh = shell.Shell(options.dry_run)
18 logging.info("Migrating %s" % dir)
19 logging.debug("uid is %d" % os.getuid())
21 deployment = make_deployment() # uses chdir
22 version = deployment.app_version
23 repo = version.application.repository(options.srv_path)
24 tag = version.scripts_tag
25 check_if_tag_exists(sh, repo, tag, version)
27 check_if_already_migrated(options)
29 os.unsetenv("GIT_DIR") # prevent some perverse errors
33 os.fdopen(os.open(".scripts-migrate-lock", os.O_CREAT | os.O_EXCL)).write(str(os.getpid()))
35 if e.errno == errno.EEXIST:
36 raise DirectoryLockedError
37 elif e.errno == errno.EACCES:
38 raise command.PermissionsError(dir)
39 make_repository(sh, options, repo, tag)
40 check_variables(deployment, options)
43 os.unlink(".scripts-migrate-lock")
47 def parse_args(argv, baton):
48 usage = """usage: %prog migrate [ARGS] DIR
50 Migrates a directory to our Git-based autoinstall format.
51 Performs basic sanity checking and intelligently determines
52 what repository and tag to use.
54 This command is meant to be run as the owner of the install
55 it is upgrading (see the scripts AFS kernel patch). Do
56 NOT run this command as root."""
57 parser = command.WizardOptionParser(usage)
58 baton.push(parser, "srv_path")
59 parser.add_option("--dry-run", dest="dry_run", action="store_true",
60 default=False, help="Prints would would be run without changing anything")
61 parser.add_option("--force", "-f", dest="force", action="store_true",
62 default=False, help="If .git or .scripts directory already exists, delete them and migrate")
63 options, args = parser.parse_all(argv)
65 parser.error("too many arguments")
67 parser.error("must specify directory")
68 return (options, args)
70 def check_if_already_migrated(options):
71 has_git = os.path.isdir(".git")
72 has_scripts = os.path.isdir(".scripts")
74 # deal with old-style migration
75 if os.path.isfile(".scripts/old-version") and not os.path.isfile(".scripts-version"):
76 os.rename(".scripts/old-version", ".scripts-version")
78 if not has_git and has_scripts:
80 raise CorruptedAutoinstallError(dir)
81 elif has_git and not has_scripts:
83 raise AlreadyVersionedError(dir)
84 elif has_git and has_scripts:
86 raise AlreadyMigratedError(dir)
89 def rm_with_backup(file):
90 prefix = "%s.bak" % file
92 for i in itertools.count():
93 name = "%s.%d" % (prefix, i)
94 if not os.path.exists(name):
98 logging.warning("Force removing .git directory")
99 if not options.dry_run:
100 rm_with_backup(".git")
102 logging.warning("Force removing .scripts directory")
103 if not options.dry_run:
104 rm_with_backup(".scripts")
106 def make_deployment():
108 return deploy.Deployment(".")
110 if e.errno == errno.ENOENT:
111 raise NotAutoinstallError(dir)
114 def check_if_tag_exists(sh, repo, tag, version):
115 # check if the version we're trying to convert exists. We assume
116 # a convention here, namely, v1.2.3-scripts is what we want. If
117 # you broke the convention... shame on you.
119 sh.call("git", "--git-dir", repo, "rev-parse", tag)
120 except shell.CallError:
121 raise NoTagError(version)
123 def make_repository(sh, options, repo, tag):
124 sh.call("git", "init") # create repository
125 # configure our alternates (to save space and make this quick)
126 data = os.path.join(repo, "objects")
127 file = ".git/objects/info/alternates"
128 if not options.dry_run:
129 alternates = open(file, "w")
130 alternates.write(data)
132 htaccess = open(".git/.htaccess", "w")
133 htaccess.write("Deny from all\n")
136 logging.info("# create %s containing \"%s\"" % (file, data))
137 logging.info('# create .htaccess containing "Deny from all"')
138 # configure our remote (this is merely for convenience; wizard scripts
139 # will not rely on this)
140 sh.call("git", "remote", "add", "origin", repo)
141 # configure what would normally be set up on a 'git clone' for consistency
142 sh.call("git", "config", "branch.master.remote", "origin")
143 sh.call("git", "config", "branch.master.merge", "refs/heads/master")
144 # perform the initial fetch
145 sh.call("git", "fetch", "origin")
146 # soft reset to our tag
147 sh.call("git", "reset", tag, "--")
148 # checkout the .scripts directory
149 sh.call("git", "checkout", ".scripts")
150 logging.info("Diffstat:\n" + sh.eval("git", "diff", "--stat"))
151 # commit user local changes
152 message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
155 message += "\nMigrated-by: " + util.get_operator_git()
156 except util.NoOperatorInfo:
158 sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
160 def check_variables(d, options):
161 """Attempt to extract variables and complain if some are missing."""
162 variables = d.extract()
163 for k,v in variables.items():
164 if v is None and k not in d.application.deprecated_keys:
165 logging.warning("Variable %s not found" % k)
167 logging.debug("Variable %s is %s" % (k,v))
169 class Error(command.Error):
170 """Base exception for all exceptions raised by migrate"""
173 class AlreadyMigratedError(Error):
174 def __init__(self, dir):
179 ERROR: Directory already contains a .git and
180 .scripts directory. If you force this migration,
181 both of these directories will be removed.
184 class AlreadyVersionedError(Error):
185 def __init__(self, dir):
190 ERROR: Directory contains a .git directory, but not
191 a .scripts directory. If this is not a corrupt
192 migration, this means that the user was versioning their
193 install using Git. You cannot force this case.
196 class CorruptedAutoinstallError(Error):
197 def __init__(self, dir):
202 ERROR: Directory contains a .scripts directory,
203 but not a .git directory. If you force this migration,
204 the .scripts directory will be removed.
207 class NotAutoinstallError(Error):
208 def __init__(self, dir):
213 ERROR: Could not find .scripts-version file. Are you sure
214 this is an autoinstalled application?
217 class DirectoryLockedError(Error):
218 def __init__(self, dir):
223 ERROR: Could not acquire lock on directory. Maybe there is
224 another migration process running?
227 class NoTagError(Error):
228 def __init__(self, version):
229 self.version = version
233 ERROR: Could not find tag v%s-scripts in repository
234 for %s. Double check and make sure
235 the repository was prepared with all necessary tags!
236 """ % (self.version.version, self.version.application.name)