]> git.ipfire.org Git - people/jschlag/pbs.git/blame - src/buildservice/sources.py
Merge branch 'master' of git://git.ipfire.org/pbs
[people/jschlag/pbs.git] / src / buildservice / sources.py
CommitLineData
9137135a
MT
1#!/usr/bin/python
2
3import datetime
4import logging
5import os
13b9276e
MT
6import pakfire
7import pakfire.config
8import shutil
9137135a 9import subprocess
13b9276e 10import tempfile
9137135a 11
2c909128 12from . import base
7ad79344 13from . import git
9137135a 14
29e226fc 15from .constants import *
78366294
MT
16from .decorators import *
17
9137135a 18class Sources(base.Object):
78366294
MT
19 def _get_source(self, query, *args):
20 res = self.db.get(query, *args)
21
22 if res:
23 return Source(self.backend, res.id, data=res)
24
25 def _get_sources(self, query, *args):
26 res = self.db.query(query, *args)
27
28 for row in res:
29 yield Source(self.backend, row.id, data=row)
30
31 def _get_commit(self, query, *args):
32 res = self.db.get(query, *args)
33
34 if res:
35 return Commit(self.backend, res.id, data=res)
36
37 def _get_commits(self, query, *args):
38 res = self.db.query(query, *args)
9137135a 39
78366294
MT
40 for row in res:
41 yield Commit(self.backend, row.id, data=row)
42
43 def __iter__(self):
44 return self._get_sources("SELECT * FROM sources")
9137135a
MT
45
46 def get_by_id(self, id):
78366294
MT
47 return self._get_source("SELECT * FROM sources \
48 WHERE id = %s", id)
9137135a
MT
49
50 def get_by_distro(self, distro):
78366294
MT
51 return self._get_sources("SELECT * FROM sources \
52 WHERE distro_id = %s", distro.id)
9137135a
MT
53
54 def update_revision(self, source_id, revision):
55 query = "UPDATE sources SET revision = %s WHERE id = %s"
56
57 return self.db.execute(query, revision, source_id)
58
f6e6ff79
MT
59 def get_commit_by_id(self, commit_id):
60 commit = self.db.get("SELECT id FROM sources_commits WHERE id = %s", commit_id)
61
62 if commit:
63 return Commit(self.pakfire, commit.id)
64
7ad79344 65 def pull(self):
78366294 66 for source in self:
29e226fc
MT
67 with git.Repo(self.backend, source, mode="mirror") as repo:
68 # Fetch the latest updates
7ad79344
MT
69 repo.fetch()
70
29e226fc
MT
71 # Import all new revisions
72 repo.import_revisions()
7ad79344 73
13b9276e 74 def dist(self):
99fc0667
MT
75 # Walk through all source repositories
76 for source in self:
77 # Get access to the git repo
78 with git.Repo(self.pakfire, source) as repo:
79 # Walk through all pending commits
80 for commit in source.pending_commits:
81 commit.state = "running"
13b9276e 82
99fc0667 83 logging.debug("Processing commit %s: %s" % (commit.revision, commit.subject))
13b9276e 84
99fc0667
MT
85 # Navigate to the right revision.
86 repo.checkout(commit.revision)
29e226fc 87
99fc0667
MT
88 # Get all changed makefiles.
89 deleted_files = []
90 updated_files = []
29e226fc 91
99fc0667
MT
92 for file in repo.changed_files(commit.revision):
93 # Don't care about files that are not a makefile.
94 if not file.endswith(".%s" % MAKEFILE_EXTENSION):
95 continue
29e226fc 96
99fc0667
MT
97 if os.path.exists(file):
98 updated_files.append(file)
99 else:
100 deleted_files.append(file)
29e226fc 101
99fc0667
MT
102 if updated_files:
103 # Create a temporary directory where to put all the files
104 # that are generated here.
105 pkg_dir = tempfile.mkdtemp()
29e226fc 106
99fc0667
MT
107 try:
108 config = pakfire.config.Config(["general.conf",])
109 config.parse(source.distro.get_config())
29e226fc 110
99fc0667 111 p = pakfire.PakfireServer(config=config)
29e226fc 112
99fc0667
MT
113 pkgs = []
114 for file in updated_files:
115 try:
116 pkg_file = p.dist(file, pkg_dir)
117 pkgs.append(pkg_file)
118 except:
119 raise
29e226fc 120
99fc0667
MT
121 # Import all packages in one swoop.
122 for pkg in pkgs:
e153d3f6
MT
123 with self.db.transaction():
124 self.backend.builds.create_from_source_package(pkg,
125 source.distro, commit=commit, type="release")
29e226fc 126
99fc0667
MT
127 except:
128 if commit:
129 commit.state = "failed"
29e226fc 130
99fc0667 131 raise
29e226fc 132
99fc0667
MT
133 finally:
134 if os.path.exists(pkg_dir):
135 shutil.rmtree(pkg_dir)
29e226fc 136
99fc0667
MT
137 for file in deleted_files:
138 # Determine the name of the package.
139 name = os.path.basename(file)
140 name = name[:len(MAKEFILE_EXTENSION) + 1]
29e226fc 141
99fc0667 142 source.distro.delete_package(name)
29e226fc 143
99fc0667
MT
144 if commit:
145 commit.state = "finished"
13b9276e 146
f6e6ff79 147
78366294
MT
148class Commit(base.DataObject):
149 table = "sources_commits"
9137135a 150
f6e6ff79
MT
151 @property
152 def revision(self):
153 return self.data.revision
154
78366294 155 @lazy_property
f6e6ff79 156 def source(self):
78366294 157 return self.backend.sources.get_by_id(self.data.source_id)
f6e6ff79
MT
158
159 @property
160 def distro(self):
161 """
162 A shortcut to the distribution this commit
163 belongs to.
164 """
165 return self.source.distro
9137135a 166
f6e6ff79 167 def set_state(self, state):
78366294 168 self._set_attribute("state", state)
9137135a 169
78366294 170 state = property(lambda s: s.data.state, set_state)
9137135a 171
f6e6ff79
MT
172 @property
173 def author(self):
174 return self.data.author
175
176 @property
177 def committer(self):
178 return self.data.committer
179
180 @property
181 def subject(self):
b9d096e0 182 return self.data.subject.strip()
f6e6ff79
MT
183
184 @property
185 def message(self):
186 return self.data.body.strip()
187
4b1e87c4
MT
188 @property
189 def message_full(self):
190 msg = [self.subject, ""] + self.message.splitlines()
191
192 return "\n".join(msg)
193
f6e6ff79
MT
194 @property
195 def date(self):
196 return self.data.date
197
78366294 198 @lazy_property
f6e6ff79 199 def packages(self):
78366294
MT
200 return self.backend.packages._get_packages("SELECT * FROM packages \
201 WHERE commit_id = %s", self.id)
f6e6ff79
MT
202
203 def reset(self):
9137135a 204 """
f6e6ff79
MT
205 Removes all packages that have been created by this commit and
206 resets the state so it will be processed again.
9137135a 207 """
f6e6ff79
MT
208 # Remove all packages and corresponding builds.
209 for pkg in self.packages:
210 # Check if there is a build associated with the package.
211 # If so, the whole build will be deleted.
212 if pkg.build:
213 pkg.build.delete()
9137135a 214
f6e6ff79
MT
215 else:
216 # Delete the package.
217 pkg.delete()
218
219 # Clear the cache.
78366294 220 del self.packages
9137135a 221
f6e6ff79
MT
222 # Reset the state to 'pending'.
223 self.state = "pending"
9137135a 224
9137135a 225
78366294
MT
226class Source(base.DataObject):
227 table = "sources"
f6e6ff79 228
78366294
MT
229 def __eq__(self, other):
230 return self.id == other.id
f6e6ff79 231
78366294
MT
232 def __len__(self):
233 ret = self.db.get("SELECT COUNT(*) AS len FROM sources_commits \
234 WHERE source_id = %s", self.id)
f6e6ff79 235
78366294 236 return ret.len
9137135a 237
78366294
MT
238 def create_commit(self, revision, author, committer, subject, body, date):
239 commit = self.backend.sources._get_commit("INSERT INTO sources_commits(source_id, \
86d8598e
MT
240 revision, author, committer, subject, body, date) VALUES(%s, %s, %s, %s, %s, %s, %s) \
241 RETURNING *", self.id, revision, author, committer, subject, body, date)
9137135a 242
78366294
MT
243 # Commit
244 commit.source = self
9137135a 245
78366294 246 return commit
9137135a 247
f6e6ff79
MT
248 @property
249 def info(self):
250 return {
251 "id" : self.id,
252 "name" : self.name,
253 "url" : self.url,
254 "path" : self.path,
255 "targetpath" : self.targetpath,
256 "revision" : self.revision,
257 "branch" : self.branch,
258 }
9137135a 259
9137135a
MT
260 @property
261 def name(self):
f6e6ff79 262 return self.data.name
9137135a
MT
263
264 @property
f6e6ff79
MT
265 def identifier(self):
266 return self.data.identifier
9137135a
MT
267
268 @property
f6e6ff79
MT
269 def url(self):
270 return self.data.url
9137135a
MT
271
272 @property
f6e6ff79
MT
273 def gitweb(self):
274 return self.data.gitweb
9137135a
MT
275
276 @property
277 def revision(self):
f6e6ff79 278 return self.data.revision
9137135a
MT
279
280 @property
281 def branch(self):
f6e6ff79 282 return self.data.branch
9137135a
MT
283
284 @property
285 def builds(self):
286 return self.pakfire.builds.get_by_source(self.id)
287
78366294 288 @lazy_property
9137135a 289 def distro(self):
f6e6ff79
MT
290 return self.pakfire.distros.get_by_id(self.data.distro_id)
291
292 @property
293 def start_revision(self):
294 return self.data.revision
295
78366294 296 @lazy_property
f6e6ff79 297 def head_revision(self):
a7d966df 298 return self.backend.sources._get_commit("SELECT * FROM sources_commits \
78366294 299 WHERE source_id = %s ORDER BY id DESC LIMIT 1", self.id)
f6e6ff79
MT
300
301 def get_commits(self, limit=None, offset=None):
a7d966df 302 return self.backend.sources._get_commits("SELECT * FROM sources_commits \
78366294 303 WHERE source_id = %s ORDER BY id DESC LIMIT %s OFFSET %s", limit, offset)
f6e6ff79
MT
304
305 def get_commit(self, revision):
a7d966df 306 commit = self.backend.sources._get_commit("SELECT * FROM sources_commits \
78366294 307 WHERE source_id = %s AND revision = %s", self.id, revision)
f6e6ff79 308
78366294
MT
309 if commit:
310 commit.source = self
311 return commit
99fc0667
MT
312
313 @property
314 def pending_commits(self):
315 return self.backend.sources._get_commits("SELECT * FROM sources_commits \
316 WHERE state = %s ORDER BY imported_at", "pending")