]>
Commit | Line | Data |
---|---|---|
677ff42a MT |
1 | #!/usr/bin/python |
2 | ||
3 | import logging | |
4 | import os | |
5 | import random | |
6 | import shutil | |
7 | import subprocess | |
8 | import xmlrpclib | |
9 | ||
10 | import pakfire.packages as packages | |
11 | import pakfire.repository as repository | |
12 | import pakfire.util as util | |
13 | from pakfire.constants import * | |
14 | ||
15 | class Source(object): | |
16 | def __init__(self, master, id, name, path, targetpath, revision, branch): | |
17 | self.master = master | |
18 | self.id = id | |
19 | self.name = name | |
20 | self.path = path | |
21 | self.targetpath = targetpath | |
22 | self.revision = revision | |
23 | self.branch = branch | |
24 | ||
25 | @property | |
26 | def pakfire(self): | |
27 | return self.master.pakfire | |
28 | ||
29 | def _git(self, cmd): | |
30 | cmd = "cd %s; git %s" % (self.path, cmd) | |
31 | ||
32 | logging.debug("Running command: %s" % cmd) | |
33 | ||
34 | return subprocess.check_output(["/bin/sh", "-c", cmd]) | |
35 | ||
36 | def _git_rev_list(self, revision=None): | |
37 | if not revision: | |
38 | revision = self.revision | |
39 | ||
a52f536c | 40 | command = "rev-list %s..origin/%s" % (revision, self.branch) |
677ff42a | 41 | |
a52f536c MT |
42 | # Get all normal commits. |
43 | commits = self._git("%s --no-merges" % command) | |
44 | commits = commits.splitlines() | |
45 | ||
46 | revisions = [] | |
47 | for commit in self._git(command).splitlines(): | |
48 | # Check if commit is a normal commit or merge commit. | |
49 | merge = not commit in commits | |
50 | ||
51 | revisions.append((commit, merge)) | |
52 | ||
53 | return reversed(revisions) | |
677ff42a MT |
54 | |
55 | def _git_changed_files(self, revision1, revision2=""): | |
56 | files = self._git("diff --name-only %s %s" % (revision1, revision2)) | |
57 | ||
58 | return [os.path.join(self.path, f) for f in files.splitlines()] | |
59 | ||
60 | def _git_checkout_revision(self, revision): | |
61 | self._git("checkout %s" % revision) | |
62 | ||
a52f536c MT |
63 | def update_revision(self, (revision, merge)): |
64 | if not merge: | |
65 | self._git_checkout_revision(revision) | |
677ff42a | 66 | |
a52f536c MT |
67 | # Get list of all changes files between the current revision and |
68 | # the previous one. | |
69 | files = self._git_changed_files("HEAD^", "HEAD") | |
677ff42a | 70 | |
a52f536c | 71 | self.update_files([f for f in files if f.endswith(".%s" % MAKEFILE_EXTENSION)]) |
677ff42a MT |
72 | |
73 | # Send update to the server. | |
a52f536c | 74 | self.master.update_revision(self, revision) |
677ff42a MT |
75 | |
76 | def update_files(self, files): | |
77 | rnd = random.randint(0, 1024**2) | |
78 | tmpdir = "/tmp/pakfire-source-%s" % rnd | |
79 | ||
80 | pkgs = [] | |
81 | for file in files: | |
82 | if os.path.exists(file): | |
83 | pkgs.append(packages.Makefile(self.pakfire, file)) | |
84 | else: | |
85 | pkg_name = os.path.basename(os.path.dirname(file)) | |
86 | ||
87 | # Send deleted package to server. | |
88 | self.master.package_remove(self, pkg_name) | |
89 | ||
90 | if not pkgs: | |
91 | return | |
92 | ||
93 | # XXX This totally ignores the local configuration. | |
94 | self.pakfire.dist(pkgs, destroy=False, resultdirs=[tmpdir,]) | |
95 | ||
96 | # Create a kind of dummy repository to link the packages against it. | |
97 | repo = repository.LocalSourceRepository(self.pakfire, | |
98 | "source-%s" % rnd, "Source packages", tmpdir, idx="directory") | |
99 | repo.update(force=True) | |
100 | ||
101 | for pkg in repo.get_all(): | |
102 | logging.debug("Processing package: %s" % pkg) | |
103 | ||
104 | pkg_path = "%(name)s/%(epoch)s-%(version)s-%(release)s/%(arch)s" % pkg.info | |
105 | ||
106 | file = os.path.join(self.targetpath, pkg_path, os.path.basename(pkg.filename)) | |
107 | dir = os.path.dirname(file) | |
108 | ||
109 | print file | |
110 | ||
111 | if os.path.exists(file): | |
112 | logging.warning("Package does already exist: %s" % file) | |
113 | ||
114 | else: | |
115 | if not os.path.exists(dir): | |
116 | os.makedirs(dir) | |
117 | ||
118 | # Copy the source file to the designated data pool. | |
119 | shutil.copy2(pkg.filename, file) | |
120 | ||
121 | # Register package in database and get an ID. | |
122 | pkg_id = self.master.package_add(self, pkg) | |
123 | ||
124 | # Re-read the package metadata (mainly update filenames). | |
125 | pkg = packages.SourcePackage(self.pakfire, repo, file) | |
126 | ||
127 | self.master.package_file_add(self, pkg_id, pkg) | |
128 | ||
129 | util.rm(tmpdir) | |
130 | ||
131 | def update(self): | |
132 | # Update files from server. | |
133 | self._git("fetch") | |
134 | ||
135 | # If there has been no data, yet we need to import all packages | |
136 | # that are currently checked out. | |
137 | if not self.revision: | |
138 | self.update_all() | |
139 | ||
140 | for rev in self._git_rev_list(): | |
141 | self.update_revision(rev) | |
142 | ||
143 | def update_all(self): | |
144 | _files = [] | |
145 | for dir, subdirs, files in os.walk(self.path): | |
146 | for f in files: | |
147 | if not f.endswith(".%s" % MAKEFILE_EXTENSION): | |
148 | continue | |
149 | ||
150 | _files.append(os.path.join(dir, f)) | |
151 | ||
152 | self.update_files(_files) | |
153 | ||
154 | ||
155 | class Master(object): | |
156 | def __init__(self, pakfire): | |
157 | self.pakfire = pakfire | |
158 | ||
159 | server = self.pakfire.config._master.get("server") | |
160 | ||
161 | logging.info("Establishing RPC connection to: %s" % server) | |
162 | ||
163 | self.conn = xmlrpclib.Server(server) | |
164 | ||
165 | def update_sources(self): | |
166 | sources = self.conn.sources_get_all() | |
167 | ||
168 | for source in sources: | |
169 | source = Source(self, **source) | |
170 | ||
171 | source.update() | |
172 | ||
173 | def update_revision(self, source, revision): | |
174 | self.conn.sources_update_revision(source.id, revision) | |
175 | ||
176 | def package_add(self, source, pkg): | |
177 | logging.info("Adding package: %s" % pkg.friendly_name) | |
178 | ||
179 | # Collect data that is sent to the database... | |
180 | info = { | |
181 | "name" : pkg.name, | |
182 | "epoch" : pkg.epoch, | |
183 | "version" : pkg.version, | |
184 | "release" : pkg.release, | |
185 | "groups" : " ".join(pkg.groups), | |
186 | "maintainer" : pkg.maintainer, | |
187 | "license" : pkg.license, | |
188 | "url" : pkg.url, | |
189 | "summary" : pkg.summary, | |
190 | "description" : pkg.description, | |
191 | "supported_arches" : pkg.supported_arches, | |
192 | "source_id" : source.id, | |
193 | } | |
194 | ||
195 | return self.conn.package_add(info) | |
196 | ||
197 | def package_file_add(self, source, pkg_id, pkg): | |
198 | logging.info("Adding package file: %s" % pkg.filename) | |
199 | ||
200 | info = { | |
201 | "path" : pkg.filename[len(source.path) + 1:], | |
202 | "source_id" : source.id, | |
203 | "type" : pkg.type, | |
204 | "arch" : pkg.arch, | |
205 | "summary" : pkg.summary, | |
206 | "description" : pkg.description, | |
207 | "requires" : " ".join(pkg.requires), | |
208 | "provides" : "", | |
209 | "obsoletes" : "", | |
210 | "conflicts" : "", | |
211 | "url" : pkg.url, | |
212 | "license" : pkg.license, | |
213 | "maintainer" : pkg.maintainer, | |
214 | "size" : pkg.size, | |
215 | "hash1" : pkg.hash1, | |
216 | "build_host" : pkg.build_host, | |
217 | "build_id" : pkg.build_id, | |
218 | "build_time" : pkg.build_time, | |
219 | "uuid" : pkg.uuid, | |
220 | } | |
221 | ||
222 | if isinstance(pkg, packages.BinaryPackage): | |
223 | info.update({ | |
224 | "provides" : " ".join(pkg.provides), | |
225 | "obsoletes" : " ".join(pkg.obsoletes), | |
226 | "conflicts" : " ".join(pkg.conflicts), | |
227 | }) | |
228 | ||
229 | return self.conn.package_file_add(pkg_id, info) | |
230 | ||
231 | def package_remove(self, source, pkg): | |
232 | logging.info("Package '%s' has been removed." % pkg) | |
233 |