5 from wizard import command, deploy, shell, util
8 options, args = parse_args(argv, baton)
11 shell.drop_priviledges(dir, options.log_file)
14 sh = shell.Shell(options.dry_run)
16 logging.info("Migrating %s" % dir)
17 logging.debug("uid is %d" % os.getuid())
19 deployment = deploy.ProductionCopy(".")
21 # deal with old-style migration, remove this later
22 if os.path.isfile(".scripts/old-version") and not os.path.isfile(".scripts-version"):
23 os.rename(".scripts/old-version", ".scripts-version")
25 os.unsetenv("GIT_DIR") # prevent some perverse errors
29 raise AlreadyMigratedError(deployment.location)
30 except deploy.NotMigratedError:
32 except (deploy.CorruptedAutoinstallError, AlreadyMigratedError):
36 deployment.verifyTag(options.srv_path)
38 if options.force_version:
39 version = deployment.application.makeVersion(options.force_version)
41 deployment.verifyVersion()
42 version = deployment.app_version
43 repo = version.application.repository(options.srv_path)
44 tag = version.scripts_tag
46 with util.LockDirectory(".scripts-migrate-lock"):
49 perform_force(options)
50 make_repository(sh, options, repo, tag)
51 check_variables(deployment, options)
52 except KeyboardInterrupt:
53 # revert it; barring zany race conditions this is safe
54 if os.path.exists(".scripts"):
55 shutil.rmtree(".scripts")
56 if os.path.exists(".git"):
59 def parse_args(argv, baton):
60 usage = """usage: %prog migrate [ARGS] DIR
62 Migrates a directory to our Git-based autoinstall format.
63 Performs basic sanity checking and intelligently determines
64 what repository and tag to use.
66 This command is meant to be run as the owner of the install
67 it is upgrading (see the scripts AFS kernel patch). Do
68 NOT run this command as root."""
69 parser = command.WizardOptionParser(usage)
70 baton.push(parser, "srv_path")
71 parser.add_option("--dry-run", dest="dry_run", action="store_true",
72 default=False, help="Prints would would be run without changing anything")
73 parser.add_option("--force", "-f", dest="force", action="store_true",
74 default=False, help="If .git or .scripts directory already exists,"
75 "delete them and migrate")
76 parser.add_option("--force-version", dest="force_version",
77 default=None, help="If .scripts-version tells lies, explicitly specify"
78 "a version to migrate to.")
79 options, args = parser.parse_all(argv)
81 parser.error("too many arguments")
83 parser.error("must specify directory")
84 return (options, args)
86 def perform_force(options):
87 has_git = os.path.isdir(".git")
88 has_scripts = os.path.isdir(".scripts")
91 logging.warning("Force removing .git directory")
92 if not options.dry_run: backup = util.safe_unlink(".git")
93 logging.info(".git backed up to %s" % backup)
95 logging.warning("Force removing .scripts directory")
96 if not options.dry_run: backup = util.safe_unlink(".scripts")
97 logging.info(".scripts backed up to %s" % backup)
99 def make_repository(sh, options, repo, tag):
100 sh.call("git", "init") # create repository
101 # configure our alternates (to save space and make this quick)
102 data = os.path.join(repo, "objects")
103 file = ".git/objects/info/alternates"
104 if not options.dry_run:
105 alternates = open(file, "w")
106 alternates.write(data)
108 htaccess = open(".git/.htaccess", "w")
109 htaccess.write("Deny from all\n")
112 logging.info("# create %s containing \"%s\"" % (file, data))
113 logging.info('# create .htaccess containing "Deny from all"')
114 # configure our remote (this is merely for convenience; wizard scripts
115 # will not rely on this)
116 sh.call("git", "remote", "add", "origin", repo)
117 # configure what would normally be set up on a 'git clone' for consistency
118 sh.call("git", "config", "branch.master.remote", "origin")
119 sh.call("git", "config", "branch.master.merge", "refs/heads/master")
120 # perform the initial fetch
121 sh.call("git", "fetch", "origin")
122 # soft reset to our tag
123 sh.call("git", "reset", tag, "--")
124 # checkout the .scripts directory
125 sh.call("git", "checkout", ".scripts")
126 logging.info("Diffstat:\n" + sh.eval("git", "diff", "--stat"))
127 # commit user local changes
128 message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
131 message += "\nMigrated-by: " + util.get_operator_git()
132 except util.NoOperatorInfo:
134 sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
136 def check_variables(d, options):
137 """Attempt to extract variables and complain if some are missing."""
138 variables = d.extract()
139 for k,v in variables.items():
140 if v is None and k not in d.application.deprecated_keys:
141 logging.warning("Variable %s not found" % k)
143 logging.debug("Variable %s is %s" % (k,v))
145 class Error(command.Error):
146 """Base exception for all exceptions raised by migrate"""
149 class AlreadyMigratedError(Error):
150 def __init__(self, dir):
155 ERROR: Directory already contains a .git and
156 .scripts directory. If you force this migration,
157 both of these directories will be removed.