7 from wizard import command, deploy, shell, util
10 options, args = parse_args(argv, baton)
14 shell.drop_priviledges(options)
16 logging.debug("uid is %d" % os.getuid())
18 check_if_already_migrated(options)
20 deployment = make_deployment() # uses chdir
21 version = deployment.app_version
22 repo = version.application.repository(options.srv_path)
23 tag = version.scripts_tag
25 os.unsetenv("GIT_DIR") # prevent some perverse errors
27 sh = shell.Shell(options.dry_run)
28 check_if_tag_exists(sh, repo, tag)
29 make_repository(sh, options, repo, tag)
30 check_variables(deployment, options)
32 if not options.dry_run:
33 deployment.scriptsifyVersion()
34 os.rename(".scripts-version", ".scripts/old-version") # archive
36 logging.info("# create .scripts/version containing \"%s-%s-scripts\"" % (deployment.application.name, deployment.version))
37 logging.info("mv .scripts-version .scripts/old-version")
39 def parse_args(argv, baton):
40 usage = """usage: %prog migrate [ARGS] DIR
42 Migrates a directory to our Git-based autoinstall format.
43 Performs basic sanity checking and intelligently determines
44 what repository and tag to use.
46 This command is meant to be run as the owner of the install
47 it is upgrading (see the scripts AFS kernel patch). Do
48 NOT run this command as root."""
49 parser = command.WizardOptionParser(usage)
50 baton.push(parser, "srv_path")
51 parser.add_option("--dry-run", dest="dry_run", action="store_true",
52 default=False, help="Prints would would be run without changing anything")
53 parser.add_option("--force", "-f", dest="force", action="store_true",
54 default=False, help="If .git or .scripts directory already exists, delete them and migrate")
55 options, args = parser.parse_all(argv)
57 parser.error("too many arguments")
59 parser.error("must specify directory")
60 return (options, args)
62 def check_if_already_migrated(options):
63 # XXX: duplicates some logic with Deployment.migrated
64 if os.path.isdir(".git") or os.path.isdir(".scripts"):
66 raise AlreadyMigratedError(dir)
68 if os.path.isdir(".git"):
69 logging.warning("Force removing .git directory")
70 if not options.dry_run: shutil.rmtree(".git")
71 if os.path.isdir(".scripts"):
72 logging.warning("Force removing .scripts directory")
73 if not options.dry_run: shutil.rmtree(".scripts")
75 def make_deployment():
77 return deploy.Deployment(".")
79 if e.errno == errno.ENOENT:
80 raise NotAutoinstallError(dir)
83 def check_if_tag_exists(sh, repo, tag):
84 # check if the version we're trying to convert exists. We assume
85 # a convention here, namely, v1.2.3-scripts is what we want. If
86 # you broke the convention... shame on you.
88 sh.call("git", "--git-dir", repo, "rev-parse", tag)
89 except shell.CallError:
90 raise NoTagError(version)
92 def make_repository(sh, options, repo, tag):
93 sh.call("git", "init") # create repository
94 # configure our alternates (to save space and make this quick)
95 data = os.path.join(repo, "objects")
96 file = ".git/objects/info/alternates"
97 if not options.dry_run:
98 alternates = open(file, "w")
99 alternates.write(data)
101 htaccess = open(".git/.htaccess", "w")
102 htaccess.write("Deny from all\n")
105 logging.info("# create %s containing \"%s\"" % (file, data))
106 logging.info('# create .htaccess containing "Deny from all"')
107 # configure our remote (this is merely for convenience; wizard scripts
108 # will not rely on this)
109 sh.call("git", "remote", "add", "origin", repo)
110 # configure what would normally be set up on a 'git clone' for consistency
111 sh.call("git", "config", "branch.master.remote", "origin")
112 sh.call("git", "config", "branch.master.merge", "refs/heads/master")
113 # perform the initial fetch
114 sh.call("git", "fetch", "origin")
115 # soft reset to our tag
116 sh.call("git", "reset", tag, "--")
117 # checkout the .scripts directory
118 sh.call("git", "checkout", ".scripts")
119 # for verbose purposes, give us a git status and git diff
122 sh.call("git", "status")
123 except shell.CallError:
126 sh.call("git", "diff")
127 except shell.CallError:
129 # commit user local changes
130 message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
133 message += "\nMigrated-by: " + util.get_operator_git()
134 except util.NoOperatorInfo:
136 sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
138 def check_variables(d, options):
139 """Attempt to extract variables and complain if some are missing."""
140 variables = d.extract()
141 for k,v in variables.items():
142 if v is None and k not in d.application.deprecated_keys:
143 logging.warning("Variable %s not found" % k)
145 logging.debug("Variable %s is %s" % (k,v))
147 class Error(command.Error):
148 """Base exception for all exceptions raised by migrate"""
151 class AlreadyMigratedError(Error):
152 def __init__(self, dir):
157 ERROR: Directory already contains a .git and/or
158 .scripts directory. Did you already migrate it?
161 class NotAutoinstallError(Error):
162 def __init__(self, dir):
167 ERROR: Could not find .scripts-version file. Are you sure
168 this is an autoinstalled application?
171 class NoTagError(Error):
172 def __init__(self, version):
173 self.version = version
177 ERROR: Could not find tag v%s-scripts in repository
178 for %s. Double check and make sure
179 the repository was prepared with all necessary tags!
180 """ % (self.version.version, self.version.application.name)