]>
git.ipfire.org Git - pbs.git/blob - src/buildservice/sources.py
16 from .constants
import *
17 from .decorators
import *
30 class Sources(base
.Object
):
31 def _get_source(self
, query
, *args
):
32 res
= self
.db
.get(query
, *args
)
35 return Source(self
.backend
, res
.id, data
=res
)
37 def _get_sources(self
, query
, *args
):
38 res
= self
.db
.query(query
, *args
)
41 yield Source(self
.backend
, row
.id, data
=row
)
43 def _get_commit(self
, query
, *args
):
44 res
= self
.db
.get(query
, *args
)
47 return Commit(self
.backend
, res
.id, data
=res
)
49 def _get_commits(self
, query
, *args
):
50 res
= self
.db
.query(query
, *args
)
53 yield Commit(self
.backend
, row
.id, data
=row
)
56 return self
._get
_sources
("SELECT * FROM sources")
58 def get_by_id(self
, id):
59 return self
._get
_source
("SELECT * FROM sources \
62 def get_by_distro(self
, distro
):
63 return self
._get
_sources
("SELECT * FROM sources \
64 WHERE distro_id = %s", distro
.id)
66 def update_revision(self
, source_id
, revision
):
67 query
= "UPDATE sources SET revision = %s WHERE id = %s"
69 return self
.db
.execute(query
, revision
, source_id
)
71 def get_commit_by_id(self
, commit_id
):
72 commit
= self
.db
.get("SELECT id FROM sources_commits WHERE id = %s", commit_id
)
75 return Commit(self
.pakfire
, commit
.id)
79 with git
.Repo(self
.backend
, source
, mode
="mirror") as repo
:
80 # Fetch the latest updates
83 # Import all new revisions
84 repo
.import_revisions()
87 # Walk through all source repositories
89 # Get access to the git repo
90 with git
.Repo(self
.pakfire
, source
) as repo
:
91 # Walk through all pending commits
92 for commit
in source
.pending_commits
:
93 commit
.state
= "running"
95 logging
.debug("Processing commit %s: %s" % (commit
.revision
, commit
.subject
))
97 # Navigate to the right revision.
98 repo
.checkout(commit
.revision
)
100 # Get all changed makefiles.
104 for file in repo
.changed_files(commit
.revision
):
105 # Don't care about files that are not a makefile.
106 if not file.endswith(".%s" % MAKEFILE_EXTENSION
):
109 if os
.path
.exists(file):
110 updated_files
.append(file)
112 deleted_files
.append(file)
115 # Create a temporary directory where to put all the files
116 # that are generated here.
117 pkg_dir
= tempfile
.mkdtemp()
120 config
= pakfire
.config
.Config(["general.conf",])
121 config
.parse(source
.distro
.get_config())
123 p
= pakfire
.PakfireServer(config
=config
)
126 for file in updated_files
:
128 pkg_file
= p
.dist(file, pkg_dir
)
129 pkgs
.append(pkg_file
)
133 # Import all packages in one swoop.
135 with self
.db
.transaction():
136 self
.backend
.builds
.create_from_source_package(pkg
,
137 source
.distro
, commit
=commit
, type="release")
141 commit
.state
= "failed"
146 if os
.path
.exists(pkg_dir
):
147 shutil
.rmtree(pkg_dir
)
149 for file in deleted_files
:
150 # Determine the name of the package.
151 name
= os
.path
.basename(file)
152 name
= name
[:len(MAKEFILE_EXTENSION
) + 1]
154 source
.distro
.delete_package(name
)
157 commit
.state
= "finished"
160 class Commit(base
.DataObject
):
161 table
= "sources_commits"
165 return self
.data
.revision
169 return self
.backend
.sources
.get_by_id(self
.data
.source_id
)
174 A shortcut to the distribution this commit
177 return self
.source
.distro
179 def set_state(self
, state
):
180 self
._set
_attribute
("state", state
)
182 state
= property(lambda s
: s
.data
.state
, set_state
)
186 return self
.backend
.users
.find_maintainer(self
.data
.author
) or self
.data
.author
190 return self
.backend
.users
.find_maintainer(self
.data
.committer
) or self
.data
.committer
194 return self
.data
.subject
.strip()
198 return self
.data
.body
.strip()
203 Returns the message without any Git tags
206 r
= re
.compile("^(%s):?" % "|".join(VALID_TAGS
), re
.IGNORECASE
)
209 for line
in self
.body
.splitlines():
210 # Find lines that start with a known Git tag
216 # If all lines are empty lines, we send back an empty message
217 if all((l
== "" for l
in message
)):
220 # We will now break the message into paragraphs
221 paragraphs
= re
.split("\n\n+", "\n".join(message
))
225 for paragraph
in paragraphs
:
226 # Remove all line breaks that are not following a colon
227 # and where the next line does not start with a star.
228 paragraph
= re
.sub("(?<=\:)\n(?=[\*\s])", " ", paragraph
)
230 message
.append(paragraph
)
232 return "\n\n".join(message
)
235 def message_full(self
):
236 message
= self
.subject
239 message
+= "\n\n%s" % self
.message
243 def get_tag(self
, tag
):
245 Returns a list of the values of this Git tag
247 if not tag
in VALID_TAGS
:
248 raise ValueError("Unknown tag: %s" % tag
)
251 r
= re
.compile("^%s:? (.*)$" % tag
, re
.IGNORECASE
)
254 for line
in self
.body
.splitlines():
255 # Skip all empty lines
259 # Check if line matches the regex
262 values
.append(m
.group(1))
267 def contributors(self
):
273 for tag
in ("Acked-by", "Cc", "Reported-by", "Reviewed-by", "Signed-off-by", "Suggested-by", "Tested-by"):
274 contributors
+= self
.get_tag(tag
)
276 # Get all user accounts that we know
277 users
= self
.backend
.users
.find_maintainers(contributors
)
279 # Add all email addresses where a user could not be found
280 for contributor
in contributors
[:]:
282 if user
.has_email_address(contributor
):
284 contributors
.remove(contributor
)
288 return sorted(contributors
+ users
)
294 for tag
in ("Acked-by", "Reviewed-by", "Signed-off-by", "Tested-by"):
295 users
+= self
.get_tag(tag
)
297 return self
.backend
.users
.find_maintainers(users
)
300 def fixed_bugs(self
):
302 Returns a list of all fixed bugs
304 return self
.get_tag("Fixes")
308 return self
.data
.date
312 return self
.backend
.packages
._get
_packages
("SELECT * FROM packages \
313 WHERE commit_id = %s", self
.id)
317 Removes all packages that have been created by this commit and
318 resets the state so it will be processed again.
320 # Remove all packages and corresponding builds.
321 for pkg
in self
.packages
:
322 # Check if there is a build associated with the package.
323 # If so, the whole build will be deleted.
328 # Delete the package.
334 # Reset the state to 'pending'.
335 self
.state
= "pending"
338 class Source(base
.DataObject
):
341 def __eq__(self
, other
):
342 return self
.id == other
.id
345 ret
= self
.db
.get("SELECT COUNT(*) AS len FROM sources_commits \
346 WHERE source_id = %s", self
.id)
350 def create_commit(self
, revision
, author
, committer
, subject
, body
, date
):
351 commit
= self
.backend
.sources
._get
_commit
("INSERT INTO sources_commits(source_id, \
352 revision, author, committer, subject, body, date) VALUES(%s, %s, %s, %s, %s, %s, %s) \
353 RETURNING *", self
.id, revision
, author
, committer
, subject
, body
, date
)
367 "targetpath" : self
.targetpath
,
368 "revision" : self
.revision
,
369 "branch" : self
.branch
,
374 return self
.data
.name
377 def identifier(self
):
378 return self
.data
.identifier
386 return self
.data
.gitweb
390 return self
.data
.revision
394 return self
.data
.branch
398 return self
.pakfire
.builds
.get_by_source(self
.id)
402 return self
.pakfire
.distros
.get_by_id(self
.data
.distro_id
)
405 def start_revision(self
):
406 return self
.data
.revision
409 def head_revision(self
):
410 return self
.backend
.sources
._get
_commit
("SELECT * FROM sources_commits \
411 WHERE source_id = %s ORDER BY id DESC LIMIT 1", self
.id)
413 def get_commits(self
, limit
=None, offset
=None):
414 return self
.backend
.sources
._get
_commits
("SELECT * FROM sources_commits \
415 WHERE source_id = %s ORDER BY id DESC LIMIT %s OFFSET %s", self
.id, limit
, offset
)
417 def get_commit(self
, revision
):
418 commit
= self
.backend
.sources
._get
_commit
("SELECT * FROM sources_commits \
419 WHERE source_id = %s AND revision = %s", self
.id, revision
)
426 def pending_commits(self
):
427 return self
.backend
.sources
._get
_commits
("SELECT * FROM sources_commits \
428 WHERE state = %s ORDER BY imported_at", "pending")